In this notebook, we will perform a causal analysis using observational data from the 1985 Current Population Survey (CPS).

The goal of this analysis is to estimate the causal effect of education on wages, taking into account potential confounding variables.

The dataset contains information on various demographic, economic, and labor-related variables.

CPS = read.csv("CPS1985.csv")

Exploratory Analysis

head(CPS, 10)

The dataset includes 12 variables that capture individual social and economic characteristics. These variables can be categorized as follows:

Numerical Variables:

Categorical Variables:

print(colSums(is.na(CPS)))
         X       wage  education experience        age  ethnicity     region     gender occupation     sector 
         0          0          0          0          0          0          0          0          0          0 
     union    married 
         0          0 

There’s no missing values across variables, so we can proceed with the analysis.

Let’s see the distribution of quantitative variables.

numerical_vars <- c("wage", "education", "experience", "age")
summary(CPS[numerical_vars])
      wage          education       experience         age       
 Min.   : 1.000   Min.   : 2.00   Min.   : 0.00   Min.   :18.00  
 1st Qu.: 5.250   1st Qu.:12.00   1st Qu.: 8.00   1st Qu.:28.00  
 Median : 7.780   Median :12.00   Median :15.00   Median :35.00  
 Mean   : 9.024   Mean   :13.02   Mean   :17.82   Mean   :36.83  
 3rd Qu.:11.250   3rd Qu.:15.00   3rd Qu.:26.00   3rd Qu.:44.00  
 Max.   :44.500   Max.   :18.00   Max.   :55.00   Max.   :64.00  
print(paste("Sample size:", nrow(CPS)))
[1] "Sample size: 534"

We can observe that the dataset includes 534 adult people of age ranging from 18 to 64 years, with 2 to 18 years of education and 0 to 55 years of work experience, so the variability among the observed sample of individuals is quite large. The same applies to wages: they range from 1 to 44.5 dollars per hour.

library(ggplot2)
library(tidyverse)
Warning: package ‘forcats’ was built under R version 4.4.1
── Attaching core tidyverse packages ─────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ lubridate 1.9.3     ✔ tibble    3.2.1
✔ purrr     1.0.2     ✔ tidyr     1.3.1
── Conflicts ───────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
# install.packages("ggiraph")
library(ggiraph)
Warning: package ‘ggiraph’ was built under R version 4.4.1
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(plotly)
Warning: package ‘plotly’ was built under R version 4.4.1
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
CPS_long <- CPS %>%
  pivot_longer(cols = c(wage, education, experience, age), names_to = "variable_name", values_to = "value")

quantiles <- CPS_long %>%
  group_by(variable_name) %>%
  summarise(
    Q1 = quantile(value, 0.25),
    Median = quantile(value, 0.50),
    Q3 = quantile(value, 0.75)
  )

density_plots <- ggplot(data = CPS_long, aes(x = value)) +
  geom_density(fill = "steelblue", color = "black", alpha = 0.7) +
  facet_wrap(~ variable_name, scales = "free") +
  labs(title = "Distribution of Numerical Variables", x = NULL, y = "Frequency") +
  theme_minimal() +
  geom_vline(data = quantiles, aes(xintercept = Q1), color = "blue", linetype = "dashed", size = 0.5) +
  geom_vline(data = quantiles, aes(xintercept = Median), color = "red", linetype = "dashed", size = 1) +
  geom_vline(data = quantiles, aes(xintercept = Q3), color = "blue", linetype = "dashed", size = 0.5)
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
ggplotly(density_plots, width = 800, height = 600)

As we can see from the plots, the distribution of wages, experience and age is right-skewed, meaning that most individuals have lower values for these variables. The distribution of education is more evenly spread, having median value at around 12 years of education.

Since the variables are skewed, there might be some outliers. Let’s have a look at the boxplots to see if there are any.

boxplots <- ggplot(CPS_long, aes(x = variable_name, y = value)) +
  geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
  facet_wrap(~ variable_name, scales = "free", ncol = 4) +
  labs(title = "Distribution of Numerical Variables", x = NULL, y = "Value") +
  theme_minimal()

ggplotly(boxplots, width = 800, height = 600)

There are visible outliers in the data:

Correlations between wages, education, experience, and age

Let’s now observe the corellation between the numerical variables of interest.

numerical_vars_df <- CPS[numerical_vars]

correlation_matrix <- cor(numerical_vars_df)

correlation_matrix_long <- as.data.frame(correlation_matrix) %>%
  rownames_to_column(var = "Var1") %>%
  gather(key = "Var2", value = "value", -Var1) %>%
  filter(Var1 != Var2) %>%
  filter(!duplicated(paste(pmin(Var1, Var2), pmax(Var1, Var2))))


print(correlation_matrix_long)

The independent variable of interest - Wage - is positively correlated with other three numerical variables with the strongest correlation for education (~0.4). Surprisingly, years of experience and age and not strongly correlated with Wage.

The strongest positive correlation is observable between age and work experience, which is not suprising if we assume that almost all individuals were working throughout their lives.

What is also interesting is that education is not strongly correlated with experience, which might indicate that people with higher education levels might not necessarily have more work experience (it makes sense if we assume that individuals who were getting their education for a longer time, might start working later).

Let’s graphically represent the relationships between wage and education, experience, and age.

We chose to apply the Loess smoothing method to this plot because it this method in non-perametric and does not assume a linear relationship between the variables. Instead, Loess fits a smoothed curve that best describes the relationship, allowing us to explore potential non-linear patterns in the data more closely. This approach is particularly suitable since we are uncertain about the exact trend of the relationship.

Additionally, the dataset’s relatively small size means that individual data points can have a significant impact. By using Loess, which is sensitive to outliers and local variations, we can better understand how these factors influence the overall trend between wage and education.

scatterplot_education <- ggplot(CPS, aes(x = education, y = wage)) +
  geom_point(size=1) +
 geom_smooth(method="auto", se=TRUE, fullrange=FALSE, level=0.95) +
  labs(title = "Education And Wage Relationship",
       x = "Years of Education",
       y = "Wage") +
  theme_minimal()

ggplotly(scatterplot_education, width = 800, height = 600)
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

If we consider the scatterplot of wage and education, we can observe a positive relationship between the two variables, that was previously observed in the correlation matrix. Furthermore, the trend is rather consistent and the variability is not too high, which indicates that the relationship might be quite strong. The consistently increasing upward slope might signal that there is also no dimishing returns to more years of educations, at least in the sectors and the occupations that we observe in this dataset.

scatterplot_experience <- ggplot(CPS, aes(x = experience, y = wage)) +
  geom_point(size=1) +
 geom_smooth(method="auto", se=TRUE, fullrange=FALSE, level=0.95) +
  labs(title = "Experience And Wage Relationship",
       x = "Years of Experience",
       y = "Wage") +
  theme_minimal()

ggplotly(scatterplot_experience, width = 800, height = 600)
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Compared to education, work experience does not show a clear relationship with wage: variability is so high that it makes the graph visually noisy and without a clear distinguishable trend. This is consistent with the correlation matrix, which showed a weaker correlation between wage and experience compared to education.

Speaking of a trend in the fitted line - there is an upward slope from 0 to ~12 years of experiences which is follwed by the flat line. It might signal that the positive linearity of the relationship is not constant across the whole career and is higher in first 10-15 years of work experience, on average. There’s also a significant drop in wage for individuals with more than ~40 years of experience, but the number of observations are relatively low in this age agroup and the variability is so high that it does not allow to make any preliminary conclusions.

Since age is highly correlated with experience, we can expect similar results for the relationship between wage and age on average, but the exact trends might differ, so it might be useful to plot it as well:

scatterplot_age <- ggplot(CPS, aes(x = age, y = wage)) +
  geom_point(size=1) +
 geom_smooth(method="auto", se=TRUE, fullrange=FALSE, level=0.95) +
  labs(title = "Experience And Wage Relationship",
       x = "Age",
       y = "Wage") +
  theme_minimal()

ggplotly(scatterplot_age, width = 800, height = 600)
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

As with the relationship between wage and experience, the relationship between wage and age is not exactly linearly positive. The fitted line shows a slight upward slope from 18 to 35 years of age, which is followed by a stagnation. It might indicate that wage gains are higher in first years of career, but then the wage does not grow or even decrease a bit. It is also consistent with the trend that we observed between wage and years of experience.

Distribution of Wages Across Different Social Groups

Let’s have a closer look at the qualitative categories that we have in this dataset.

categorical_vars <- c("ethnicity", "region", "gender", "occupation", "sector", "union", "married")

for (var in categorical_vars) {
  cat("Categories in", var, ":\n")
  print(unique(CPS[[var]]))
  cat("\n")
}
Categories in ethnicity :
[1] "hispanic" "cauc"     "other"   

Categories in region :
[1] "other" "south"

Categories in gender :
[1] "female" "male"  

Categories in occupation :
[1] "worker"     "management" "sales"      "office"     "services"   "technical" 

Categories in sector :
[1] "manufacturing" "other"         "construction" 

Categories in union :
[1] "no"  "yes"

Categories in married :
[1] "yes" "no" 

It might be interesting to observe differences in wage distribution across different social groups separately.

Sectors and Occupations

Let’s start with professional occupations and sectors.

We suspect that there is a significant difference in wages among individuals in different occupations and industries, since some sectors and occupations require more advanced and/or specialized skills than others.

wage_by_sector <- ggplot(CPS, aes(x = sector, y = wage)) +
  geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
  labs(title = "Distribution of Wages Across Sectors",
       x = "Sector",
       y = "Wage") +
  theme_minimal()

ggplotly(wage_by_sector, width = 800, height = 600)

Median and interquartile ranges are quite close across sectors. As it could have been expected, “other” category (which includes all data points outside of construction and manufacturing) have the largest and biggest outliers, but the lowest median. Construction sector has the highest median wage and the lowest variability in wages.

summary_sectors <- CPS %>%
  group_by(sector) %>%
  summarise(
    Mean = mean(wage),
    Median = median(wage),
    SD = sd(wage),
    IQR = IQR(wage, na.rm = TRUE),
    Count = n()
  )

summary_sectors

Despite significant difference in number of observations across sectors (“other” category is significantly larger in number of observations), the sectors appear to be comparable in terms of mean and median.

The standard deviation is the highest in the “other” category, which is consistent with the boxplot. The construction sector has the lowest standard deviation.

According to the calculated IQRs across sectors, manufacturing has the highest variability of the middle 50% of the data, which is consistent with the boxplot. Construction sector has the lowest variability, which is also consistent with the boxplot.

Overall, despire rather closed measures of central tendency, the measures of variability are quite different across sectors, which might indicate that sector of employemtn is an important predictor of wage level.

It is also likely to be a common cause confounder for the relationship between education and wages, since people working in different sectors might have different levels of education and wages. it might be especially the case for “other” category since it includes a wide variety of industries.

# install.packages("forcats")
library(forcats)
library(dplyr)

CPS <- CPS %>%
  mutate(occupation = fct_reorder(occupation, wage, .fun = median))

wage_by_occupation <- ggplot(CPS, aes(x = occupation, y = wage)) +
  geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
  labs(title = "Distribution of Wages Across Occupations",
       x = "Occupation",
       y = "Wage") +
  theme_minimal()

ggplotly(wage_by_occupation, width = 800, height = 600)
summary_occupations <- CPS %>%
  group_by(occupation) %>%
  summarise(
    Mean = mean(wage),
    Median = median(wage),
    SD = sd(wage),
    IQR = IQR(wage, na.rm = TRUE),
    Count = n()
  )

summary_occupations

Highest median and interquartile ranges are observable in the Management and Technical groups, which means they have higher hourly wage for a “typical” worker as well as larger variability in wages. These two types of occupations often require advanced level education and specialized skills, which can explain the higher wages.

Worker and Sales occupations have quite long tails with relatively small numbers of very high earners. Successful sales specialists can have high incomes from commissions, while some highly qualified workers can have much higher hourly wages, than average workers due to their high level or highly specialized skills.

Overall, there is quite high variability, average and median differences in wages across these groups, which might indicate that occupation is a very important predictor of wage level. It is also likely to be a common cause confounder for the relationship between education and wages, since people with higher education levels might be more likely to work in higher paid sectors and occupations (it especially holds true for management and technical groups that might require advanced level education).

Ethnic groups

Let’s now observe the distribution of wages across different demographic groups.

CPS <- CPS %>%
  mutate(ethnicity = fct_reorder(ethnicity, wage, .fun = median))

wage_by_ethnicity <- ggplot(CPS, aes(x = ethnicity, y = wage)) +
  geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
  labs(title = "Distribution of Wages Across Ethnic Groups",
       x = "Occupation",
       y = "Wage") +
  theme_minimal()

ggplotly(wage_by_ethnicity, width = 800, height = 600)
summary_ethnicity <- CPS %>%
  group_by(ethnicity) %>%
  summarise(
    Mean = mean(wage),
    Median = median(wage),
    SD = sd(wage),
    IQR = IQR(wage, na.rm = TRUE),
    Count = n()
  )

summary_ethnicity

Caucasians have both the highest median wage and the highest variability in wages.

Higher variability can be explained by the fact that the majority of the sample is Caucasian, so the distribution of wages is more spread out. Additionally, there might structural differences in incomes between different ethnic groups. For instance, non-white minorities have lower access to higher paid sectors and occupations, which can lead to lower wages.

library(RColorBrewer)

ethnicity_colors <- c("hispanic" = "brown4", "other" = "darkgoldenrod", "cauc" = "cadetblue")

ethnicity_occupation_plot <- ggplot(CPS, aes(x = occupation, fill = ethnicity)) +
  geom_bar(position = "fill") +
  labs(title = "Ethnic Composition Across Occupations",
       x = "Occupation",
       y = "Proportion",
       fill = "Ethnicity") +
  scale_y_continuous(labels = scales::percent) +
  scale_fill_manual(values = ethnicity_colors) +
  theme_minimal()


ggplotly(ethnicity_occupation_plot)

As we can see, among occupation groups, non-causasians’ proportions are the highest in the office, worker and services occupation groups, which are associated with lower wages, as we saw previously.

Generally speaking, ethnic origin might be a very important predictor of a wage level. Additionally, it might also affect the access to education, which means that it is a common cause confounder.

Gender

Let’s observe the distribution of wages across gender. Gender disparity is widely discussed in the economic literature, so it might be interesting to see if there is a wage gap between two genders in this dataset. We would expect a significant difference, especially since the data comes from 80’s when gender pay gap was even more pronounced.

wage_by_gender <- ggplot(CPS, aes(x = gender, y = wage)) +
  geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
  labs(title = "Distribution of Wages Across Gender Groups",
       x = "Gender",
       y = "Wage") +
  theme_minimal()

ggplotly(wage_by_gender, width = 800, height = 600)

Even though both median and interquartile ranges are higher for males, there is quite a lot of outliers in the female groups, indicating a small number of high earners among females.

summary_gender <- CPS %>%
  group_by(gender) %>%
  summarise(
    Mean = mean(wage),
    Median = median(wage),
    SD = sd(wage),
    IQR = IQR(wage, na.rm = TRUE),
    Count = n()
  )

summary_gender

Despite outliers mentioned earlier, the standard deviation and interquartile range of the male group signal higher variability in wages.

Again, we would expect gender to be a common cause confounder for the relationship between education and wages, because it can affect both the access to education, number of years of formal education (due to different social norms and expectations applied to females) and the wage level (due to glass ceilings).

Location

The data also allows to observe the differences between regions in terms of wages. The dataset includes 2 categories related to location: south and other. We would expect that there is a wage gap between these two regions, since the South is generally considered to be more rural, less developed, resulting in lower average wages.

wage_by_region <- ggplot(CPS, aes(x = region, y = wage)) +
  geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
  labs(title = "Distribution of Wages Across Regions",
       x = "Region",
       y = "Wage") +
  theme_minimal()

ggplotly(wage_by_region, width = 800, height = 600)

It’s vividly visible that both the median and the range are higher in the “other” category, which is consistent with the assumption that the South has lower wages.

summary_region <- CPS %>%
  group_by(region) %>%
  summarise(
    Mean = mean(wage),
    Median = median(wage),
    SD = sd(wage),
    IQR = IQR(wage, na.rm = TRUE),
    Count = n()
  )

summary_region

Unionization

Unionization is another important factor that can affect wages: union member are protected by the collective agreements and usually have higher wages.

wage_by_union <- ggplot(CPS, aes(x = union, y = wage)) +
  geom_boxplot(fill = "steelblue", color = "black", alpha = 0.7) +
  labs(title = "Distribution of Wages Across Union Memberships",
       x = "Union Membership",
       y = "Wage") +
  theme_minimal()

ggplotly(wage_by_union, width = 800, height = 600)

Causal Analysis

After preliminary correlation and exploratory analysis, we can now proceed with the hypothesis testing. Education was found to have the highest positive correlation with wages among quantitative variables (correlations between wages and age and experience were weaker). Qualitative variables, such as occupation and sector, also showed some differences in wage distribution and their relationship with wages might be interesting to explore further.

The set of hypotheses is formulated in the following way:

Let’s have a look at the relationship between the level of education, controlling for experience and age, and wages.

First, since age and experience are highly correlated, we will check for multicollinearity between these two variables, using Variance Inflation Factor (VIF). Let’s create models with and wothout collinear independent variables and compare the results:

# install.packages("stargazer")
library(stargazer)

Please cite as: 

 Hlavac, Marek (2022). stargazer: Well-Formatted Regression and Summary Statistics Tables.
 R package version 5.2.3. https://CRAN.R-project.org/package=stargazer 
model_numerical_1 <- lm(wage ~ education + experience + age, data = CPS)
model_numerical_2 <- lm(wage ~ education + experience, data = CPS)
model_numerical_3 <- lm(wage ~ education + age, data = CPS)

stargazer(model_numerical_1, model_numerical_2, model_numerical_3,
          type = "text",
          title = "Wage Vs. Education, Experience & Age",
          align = TRUE,
          column.labels = c("All numerical", "w/o Age", "w/o Experience"))

Wage Vs. Education, Experience & Age
===========================================================================================
                                              Dependent variable:                          
                    -----------------------------------------------------------------------
                                                     wage                                  
                         All numerical              w/o Age             w/o Experience     
                              (1)                     (2)                     (3)          
-------------------------------------------------------------------------------------------
education                    0.948                 0.926***                0.821***        
                            (1.155)                 (0.081)                 (0.077)        
                                                                                           
experience                   0.128                 0.105***                                
                            (1.156)                 (0.017)                                
                                                                                           
age                         -0.022                                         0.105***        
                            (1.155)                                         (0.017)        
                                                                                           
Constant                    -4.770                 -4.904***               -5.534***       
                            (7.043)                 (1.219)                 (1.279)        
                                                                                           
-------------------------------------------------------------------------------------------
Observations                  534                     534                     534          
R2                           0.202                   0.202                   0.202         
Adjusted R2                  0.198                   0.199                   0.199         
Residual Std. Error    4.604 (df = 530)        4.599 (df = 531)        4.599 (df = 531)    
F Statistic         44.727*** (df = 3; 530) 67.217*** (df = 2; 531) 67.210*** (df = 2; 531)
===========================================================================================
Note:                                                           *p<0.1; **p<0.05; ***p<0.01

First model includes all three variables, while the second model includes only education and experience (excluding age). The third model includes only education and age (excluding experience).

First model demonstrates highly inflated standard errors, most likely due to multicollinearity between experience and age.

Both 2nd and 2rd models show and statistical and quite high correlation between education and wage, while the respective coefficients for isolated experience and age variables are quite similar. it is indeed a sign of multicollinearity.

Let’s see the Variance Inflation Factor to check the power of multicollinearity in the 1st model:

# install.packages("car")
library(car)
Warning: package ‘car’ was built under R version 4.4.1
Loading required package: carData
Warning: package ‘carData’ was built under R version 4.4.1

Attaching package: ‘car’

The following object is masked from ‘package:dplyr’:

    recode

The following object is masked from ‘package:purrr’:

    some
vif1 <- vif(model_numerical_1)
print(vif1)
 education experience        age 
  229.5738  5147.9190  4611.4008 

The calculated VIF is extremely high, which indicates that multicollinearity is a significant issue in the model.

Let’s check VIF for the 2nd and 3rd models:

vif2 <- vif(model_numerical_2)
print(vif2)
 education experience 
  1.142049   1.142049 
vif3 <- vif(model_numerical_3)
print(vif3)
education       age 
 1.023024  1.023024 

Both models have VIF values ~1, which is a sign of no multicollinearity.

Let’s observe the correlations between wage and socio-economic characteristics of the individuals.


model_categorical <- lm(wage ~ ethnicity + region + gender + occupation + sector 
+ union + married, data = CPS)

stargazer(model_categorical, type = "text", title = "Wage Vs. Categorical variables", align = TRUE)

Wage Vs. Categorical variables
================================================
                         Dependent variable:    
                     ---------------------------
                                wage            
------------------------------------------------
ethnicityother                  0.771           
                               (1.023)          
                                                
ethnicitycauc                  1.623*           
                               (0.893)          
                                                
regionsouth                    -0.824*          
                               (0.436)          
                                                
gendermale                    2.028***          
                               (0.434)          
                                                
occupationsales                 0.761           
                               (0.893)          
                                                
occupationworker                0.188           
                               (0.704)          
                                                
occupationoffice               1.388**          
                               (0.678)          
                                                
occupationtechnical           4.752***          
                               (0.669)          
                                                
occupationmanagement          5.691***          
                               (0.794)          
                                                
sectormanufacturing             0.714           
                               (1.033)          
                                                
sectorother                    -0.564           
                               (1.003)          
                                                
unionyes                      2.018***          
                               (0.531)          
                                                
marriedyes                      0.608           
                               (0.413)          
                                                
Constant                      4.392***          
                               (1.480)          
                                                
------------------------------------------------
Observations                     534            
R2                              0.261           
Adjusted R2                     0.243           
Residual Std. Error       4.471 (df = 520)      
F Statistic           14.162*** (df = 13; 520)  
================================================
Note:                *p<0.1; **p<0.05; ***p<0.01

Since it is not an entire model of the relationship we are interested in, we can’t make any conclusions about the effect of these variables on wages. However, it is valuable to make preliminary observations on how these common cause confounders correlated with the wages.

As we can see:

This check is consistent with the assumptions we had about the confounding variables before. So far the occupation is the strongest predictor of wage level among other confounders. It might also indicate that education indeed translates into higher wages, because technical and manegerial positions usually require higher level of education. At the same time, there is some association with the social position of the individual: ethnicity, location, gender and union membership are also associated with wage level.

Models with confounderd included

Let’s run the models with the confounders included. We don’t include age due to its high collinearity with experience.

# num + gender
model_full1 <- lm(wage ~ education + experience + gender, data = CPS)
# num + ethnicity 
model_full2 <- lm(wage ~ education + experience + ethnicity, data = CPS)
# num + gender + ethnicity 
model_full3 <- lm(wage ~ education + experience + gender + ethnicity, data = CPS)

# model_full <- lm(wage ~ education + experience + gender + ethnicity + region + occupation + sector+ union + married, data = CPS)

stargazer(model_full1, model_full2, model_full3, type = "text", title = "Wage Level predictors", align = TRUE)

Wage Level predictors
===========================================================================================
                                              Dependent variable:                          
                    -----------------------------------------------------------------------
                                                     wage                                  
                              (1)                     (2)                     (3)          
-------------------------------------------------------------------------------------------
education                  0.941***                0.915***                0.930***        
                            (0.079)                 (0.082)                 (0.080)        
                                                                                           
experience                 0.113***                0.105***                0.113***        
                            (0.017)                 (0.017)                 (0.017)        
                                                                                           
gendermale                 2.338***                                        2.356***        
                            (0.388)                                         (0.388)        
                                                                                           
ethnicityother                                      -0.441                  -0.622         
                                                    (1.054)                 (1.020)        
                                                                                           
ethnicitycauc                                        0.409                   0.336         
                                                    (0.923)                 (0.893)        
                                                                                           
Constant                   -6.505***               -5.041***               -6.576***       
                            (1.210)                 (1.403)                 (1.382)        
                                                                                           
-------------------------------------------------------------------------------------------
Observations                  534                     534                     534          
R2                           0.253                   0.205                   0.257         
Adjusted R2                  0.249                   0.199                   0.250         
Residual Std. Error    4.454 (df = 530)        4.599 (df = 529)        4.451 (df = 528)    
F Statistic         59.885*** (df = 3; 530) 34.131*** (df = 4; 529) 36.527*** (df = 5; 528)
===========================================================================================
Note:                                                           *p<0.1; **p<0.05; ***p<0.01

In these models we have added social confounders that are hypethesized to be important:

Now, let’s examine the model with a categorical variable for occupation included. We expect that this variable will be a significant predictor of wage level, since it is a common cause confounder for the relationship between education and wages.

model_full4 <- lm(wage ~ education + experience + gender + occupation, data = CPS)

stargazer(model_full1, model_full4, type = "text", title = "Wage Level predictors", align = TRUE)

Wage Level predictors
====================================================================
                                   Dependent variable:              
                     -----------------------------------------------
                                          wage                      
                               (1)                     (2)          
--------------------------------------------------------------------
education                   0.941***                0.717***        
                             (0.079)                 (0.099)        
                                                                    
experience                  0.113***                0.103***        
                             (0.017)                 (0.017)        
                                                                    
gendermale                  2.338***                1.965***        
                             (0.388)                 (0.416)        
                                                                    
occupationsales                                      -0.199         
                                                     (0.862)        
                                                                    
occupationworker                                     1.427**        
                                                     (0.612)        
                                                                    
occupationoffice                                      0.577         
                                                     (0.665)        
                                                                    
occupationtechnical                                 2.818***        
                                                     (0.738)        
                                                                    
occupationmanagement                                3.840***        
                                                     (0.806)        
                                                                    
Constant                    -6.505***               -4.662***       
                             (1.210)                 (1.397)        
                                                                    
--------------------------------------------------------------------
Observations                   534                     534          
R2                            0.253                   0.302         
Adjusted R2                   0.249                   0.291         
Residual Std. Error     4.454 (df = 530)        4.327 (df = 525)    
F Statistic          59.885*** (df = 3; 530) 28.351*** (df = 8; 525)
====================================================================
Note:                                    *p<0.1; **p<0.05; ***p<0.01

In the last model “services” occupation is designated as a reference category. The coefficients for the other categories are interpreted as the difference in wage level compared to “services”. Since the “services” had the lowest median and the interquartile range, it is a good fit for a reference category as the group with lowest earnings. As expected, technical and managerial positions are associated with higher wages, increasing the wage by ~2.8 and ~3.8 dollars respectively on average. SO far, this relationship is higher than education, experience and gender.

The model with occupation included shows a jump in R squared (from 0.253 to 0.302) and significant change in the coefficents for education and gender: the magnitude of both coefficents decreased after controlling for the occupation.

There is a chance of collinearity between occupation and education, that should be checked:

vif4 <- vif(model_full4)
print(vif4)
               GVIF Df GVIF^(1/(2*Df))
education  1.910504  1        1.382210
experience 1.188824  1        1.090332
gender     1.227183  1        1.107783
occupation 2.056254  5        1.074751

Occupation shows low multicollinearity, which is acceptable.

# install.packages("lmtest")
library(lmtest)
bptest(model_full1)

    studentized Breusch-Pagan test

data:  model_full1
BP = 9.0939, df = 3, p-value = 0.02807
bptest(model_full4)

    studentized Breusch-Pagan test

data:  model_full4
BP = 22.39, df = 8, p-value = 0.004242

Breusch-Pagan test for heteroscedasticity shows that both models have heteroscedasticity, which means that the standard errors are not constant. It might be a sign of omitted variables.

plot(model_full4$fitted.values, model_full4$residuals,
     main = "Residuals vs Fitted: Model with Gender & Occupation",
     xlab = "Fitted Values",
     ylab = "Residuals")
abline(h = 0, col = "red")

Let’s now add new confounders to the model 4.

model_full5 <- lm(wage ~ education + experience + gender + occupation + sector, data = CPS)
stargazer(model_full4 ,model_full5, type = "text", title = "Wage Level predictors", align = TRUE)

Wage Level predictors
=====================================================================
                                   Dependent variable:               
                     ------------------------------------------------
                                           wage                      
                               (1)                     (2)           
---------------------------------------------------------------------
education                   0.717***                 0.710***        
                             (0.099)                 (0.099)         
                                                                     
experience                  0.103***                 0.100***        
                             (0.017)                 (0.017)         
                                                                     
gendermale                  1.965***                 2.025***        
                             (0.416)                 (0.419)         
                                                                     
occupationsales              -0.199                   -0.284         
                             (0.862)                 (0.862)         
                                                                     
occupationworker             1.427**                  0.907          
                             (0.612)                 (0.679)         
                                                                     
occupationoffice              0.577                   0.529          
                             (0.665)                 (0.665)         
                                                                     
occupationtechnical         2.818***                 2.723***        
                             (0.738)                 (0.740)         
                                                                     
occupationmanagement        3.840***                 3.754***        
                             (0.806)                 (0.807)         
                                                                     
sectormanufacturing                                   0.479          
                                                     (0.996)         
                                                                     
sectorother                                           -0.539         
                                                     (0.973)         
                                                                     
Constant                    -4.662***                -4.029**        
                             (1.397)                 (1.712)         
                                                                     
---------------------------------------------------------------------
Observations                   534                     534           
R2                            0.302                   0.306          
Adjusted R2                   0.291                   0.293          
Residual Std. Error     4.327 (df = 525)         4.321 (df = 523)    
F Statistic          28.351*** (df = 8; 525) 23.085*** (df = 10; 523)
=====================================================================
Note:                                     *p<0.1; **p<0.05; ***p<0.01

Adding sector as a predictor does not seem to produce any significant changes in the model. The coefficients for other predictors remain quite similar, while sector dummy variables are not significant.

model_full6 <- lm(wage ~ education + experience + gender + occupation + region, data = CPS)
stargazer(model_full4, model_full6, type = "text", title = "Wage Level predictors", align = TRUE)

Wage Level predictors
====================================================================
                                   Dependent variable:              
                     -----------------------------------------------
                                          wage                      
                               (1)                     (2)          
--------------------------------------------------------------------
education                   0.717***                0.694***        
                             (0.099)                 (0.100)        
                                                                    
experience                  0.103***                0.101***        
                             (0.017)                 (0.016)        
                                                                    
gendermale                  1.965***                1.990***        
                             (0.416)                 (0.415)        
                                                                    
occupationsales              -0.199                  -0.137         
                             (0.862)                 (0.861)        
                                                                    
occupationworker             1.427**                 1.430**        
                             (0.612)                 (0.611)        
                                                                    
occupationoffice              0.577                   0.638         
                             (0.665)                 (0.664)        
                                                                    
occupationtechnical         2.818***                2.825***        
                             (0.738)                 (0.737)        
                                                                    
occupationmanagement        3.840***                3.832***        
                             (0.806)                 (0.804)        
                                                                    
regionsouth                                          -0.791*        
                                                     (0.417)        
                                                                    
Constant                    -4.662***               -4.139***       
                             (1.397)                 (1.421)        
                                                                    
--------------------------------------------------------------------
Observations                   534                     534          
R2                            0.302                   0.306         
Adjusted R2                   0.291                   0.295         
Residual Std. Error     4.327 (df = 525)        4.316 (df = 524)    
F Statistic          28.351*** (df = 8; 525) 25.726*** (df = 9; 524)
====================================================================
Note:                                    *p<0.1; **p<0.05; ***p<0.01

Adding region (which shows either living in the North or in the South of the US) as a predictor shows that living in the South is associated with lower wages. It has also slighly altered the magnitude of other predictors, but their coefficients remain significant.

model_full7 <- lm(wage ~ education + experience + gender + occupation + region + union, data = CPS)
stargazer(model_full6, model_full7, type = "text", title = "Wage Level predictors", align = TRUE)

Wage Level predictors
=====================================================================
                                   Dependent variable:               
                     ------------------------------------------------
                                           wage                      
                               (1)                     (2)           
---------------------------------------------------------------------
education                   0.694***                 0.672***        
                             (0.100)                 (0.099)         
                                                                     
experience                  0.101***                 0.094***        
                             (0.016)                 (0.017)         
                                                                     
gendermale                  1.990***                 1.845***        
                             (0.415)                 (0.415)         
                                                                     
occupationsales              -0.137                   0.173          
                             (0.861)                 (0.860)         
                                                                     
occupationworker             1.430**                 1.349**         
                             (0.611)                 (0.607)         
                                                                     
occupationoffice              0.638                   0.801          
                             (0.664)                 (0.661)         
                                                                     
occupationtechnical         2.825***                 2.880***        
                             (0.737)                 (0.731)         
                                                                     
occupationmanagement        3.832***                 4.148***        
                             (0.804)                 (0.805)         
                                                                     
regionsouth                  -0.791*                 -0.689*         
                             (0.417)                 (0.415)         
                                                                     
unionyes                                             1.517***        
                                                     (0.508)         
                                                                     
Constant                    -4.139***               -4.014***        
                             (1.421)                 (1.411)         
                                                                     
---------------------------------------------------------------------
Observations                   534                     534           
R2                            0.306                   0.318          
Adjusted R2                   0.295                   0.305          
Residual Std. Error     4.316 (df = 524)         4.284 (df = 523)    
F Statistic          25.726*** (df = 9; 524) 24.394*** (df = 10; 523)
=====================================================================
Note:                                     *p<0.1; **p<0.05; ***p<0.01

Being a union member appears to have a significant posaitive relationship with the wage level (as was theorized before constructing the model). Adding predictor for union membership has also slightly altered the magnitude of other predictors, but their coefficients still remain significant.

Interestingly, the magnitude affected the most is the occupation: the coefficients for managerial positions have increased.

model_full8 <- lm(wage ~ education + experience + gender + occupation + region + union + married, data = CPS)
stargazer(model_full7, model_full8, type = "text", title = "Wage Level predictors", align = TRUE)

Wage Level predictors
======================================================================
                                    Dependent variable:               
                     -------------------------------------------------
                                           wage                       
                               (1)                      (2)           
----------------------------------------------------------------------
education                    0.672***                 0.672***        
                             (0.099)                  (0.099)         
                                                                      
experience                   0.094***                 0.090***        
                             (0.017)                  (0.017)         
                                                                      
gendermale                   1.845***                 1.852***        
                             (0.415)                  (0.415)         
                                                                      
occupationsales               0.173                    0.099          
                             (0.860)                  (0.865)         
                                                                      
occupationworker             1.349**                  1.316**         
                             (0.607)                  (0.608)         
                                                                      
occupationoffice              0.801                    0.780          
                             (0.661)                  (0.662)         
                                                                      
occupationtechnical          2.880***                 2.822***        
                             (0.731)                  (0.735)         
                                                                      
occupationmanagement         4.148***                 4.098***        
                             (0.805)                  (0.808)         
                                                                      
regionsouth                  -0.689*                  -0.697*         
                             (0.415)                  (0.415)         
                                                                      
unionyes                     1.517***                 1.487***        
                             (0.508)                  (0.510)         
                                                                      
marriedyes                                             0.338          
                                                      (0.410)         
                                                                      
Constant                    -4.014***                -4.124***        
                             (1.411)                  (1.418)         
                                                                      
----------------------------------------------------------------------
Observations                   534                      534           
R2                            0.318                    0.319          
Adjusted R2                   0.305                    0.305          
Residual Std. Error      4.284 (df = 523)         4.286 (df = 522)    
F Statistic          24.394*** (df = 10; 523) 22.224*** (df = 11; 522)
======================================================================
Note:                                      *p<0.1; **p<0.05; ***p<0.01

Being married does not appear to be a significant predictor of wage level in this model, which is consistent with our preliminary assumptions.

Education, experience, gender, occupation (in technical, management roles), and union membership are significant predictors of wages.

library(car)
print(vif(model_full))
               GVIF Df GVIF^(1/(2*Df))
education  2.016088  1        1.419890
experience 1.328896  1        1.152778
ethnicity  1.094358  2        1.022798
region     1.060870  1        1.029985
gender     1.268547  1        1.126298
occupation 3.000128  5        1.116128
sector     1.444433  2        1.096287
union      1.128691  1        1.062399
married    1.109060  1        1.053119

VIF values are quite low, which indicates that multicollinearity is not an issue in this model, after excluding age.

library(ggplot2)
model_full_residuals <- ggplot(CPS, aes(x = fitted(model_full), y = resid(model_full))) +
  geom_point() +
  geom_hline(yintercept = 0, linetype = "dashed", color = "red") +
  labs(x = "Fitted Values", y = "Residuals", title = "Residuals vs. Fitted Values Plot") +
  theme_minimal()

ggplotly(model_full_residuals, width = 800, height = 600)

There is widening?

There’s a number of variables that appeared to be non-significant: sector & marriage. Let’s constrcut a model without them and compare the results.

model_wo_marriage_sector_region <- lm(wage ~ education + experience + ethnicity +  gender + occupation + union, data = CPS)

stargazer(model_full, model_wo_marriage_sector_region, type = "text", title = "Wage Level predictors", align = TRUE)

Wage Level predictors
======================================================================
                                    Dependent variable:               
                     -------------------------------------------------
                                           wage                       
                               (1)                      (2)           
----------------------------------------------------------------------
education                    0.655***                 0.675***        
                             (0.101)                  (0.100)         
                                                                      
experience                   0.087***                 0.094***        
                             (0.017)                  (0.017)         
                                                                      
ethnicityother                -0.230                   -0.152         
                             (0.991)                  (0.991)         
                                                                      
ethnicitycauc                 0.608                    0.728          
                             (0.869)                  (0.867)         
                                                                      
regionsouth                   -0.564                                  
                             (0.419)                                  
                                                                      
gendermale                   1.939***                 1.850***        
                             (0.418)                  (0.416)         
                                                                      
occupationsales               -0.087                   0.021          
                             (0.868)                  (0.864)         
                                                                      
occupationworker              0.687                   1.226**         
                             (0.678)                  (0.611)         
                                                                      
occupationoffice              0.707                    0.730          
                             (0.663)                  (0.662)         
                                                                      
occupationtechnical          2.649***                 2.791***        
                             (0.742)                  (0.737)         
                                                                      
occupationmanagement         3.977***                 4.121***        
                             (0.810)                  (0.808)         
                                                                      
sectormanufacturing           0.562                                   
                             (0.991)                                  
                                                                      
sectorother                   -0.478                                  
                             (0.965)                                  
                                                                      
unionyes                     1.601***                 1.662***        
                             (0.512)                  (0.509)         
                                                                      
marriedyes                    0.297                                   
                             (0.410)                                  
                                                                      
Constant                     -3.872**                -4.784***        
                             (1.840)                  (1.496)         
                                                                      
----------------------------------------------------------------------
Observations                   534                      534           
R2                            0.326                    0.318          
Adjusted R2                   0.307                    0.304          
Residual Std. Error      4.278 (df = 518)         4.288 (df = 522)    
F Statistic          16.738*** (df = 15; 518) 22.148*** (df = 11; 522)
======================================================================
Note:                                      *p<0.1; **p<0.05; ***p<0.01

Check Model Fit and Diagnostics

  1. overfitting?
  2. Check residuals to ensure the assumptions of linearity, homoscedasticity, and normality hold.
  3. y - y plot
  4. Consider Interaction Effects

Do outliers affect the results? - Cook’s Distance - Robustness checks

Limitations

LS0tDQp0aXRsZTogIkRvZXMgRWR1Y2F0aW9uIEFmZmVjdCBXYWdlcz8gQSBDYXVzYWwgQW5hbHlzaXMgVXNpbmcgMTk4NSBDUFMgRGF0YSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCkluIHRoaXMgbm90ZWJvb2ssIHdlIHdpbGwgcGVyZm9ybSBhIGNhdXNhbCBhbmFseXNpcyB1c2luZyBvYnNlcnZhdGlvbmFsIGRhdGEgZnJvbSB0aGUgMTk4NSBDdXJyZW50IFBvcHVsYXRpb24gU3VydmV5IChDUFMpLg0KDQpUaGUgZ29hbCBvZiB0aGlzIGFuYWx5c2lzIGlzIHRvIGVzdGltYXRlIHRoZSBjYXVzYWwgZWZmZWN0IG9mIGVkdWNhdGlvbiBvbiB3YWdlcywgdGFraW5nIGludG8gYWNjb3VudCBwb3RlbnRpYWwgY29uZm91bmRpbmcgdmFyaWFibGVzLg0KDQpUaGUgZGF0YXNldCBjb250YWlucyBpbmZvcm1hdGlvbiBvbiB2YXJpb3VzIGRlbW9ncmFwaGljLCBlY29ub21pYywgYW5kIGxhYm9yLXJlbGF0ZWQgdmFyaWFibGVzLg0KDQpgYGB7cn0NCkNQUyA9IHJlYWQuY3N2KCJDUFMxOTg1LmNzdiIpDQpgYGANCg0KIyMgRXhwbG9yYXRvcnkgQW5hbHlzaXMNCmBgYHtyfQ0KaGVhZChDUFMsIDEwKQ0KYGBgDQpUaGUgZGF0YXNldCBpbmNsdWRlcyAxMiB2YXJpYWJsZXMgdGhhdCBjYXB0dXJlIGluZGl2aWR1YWwgc29jaWFsIGFuZCBlY29ub21pYyBjaGFyYWN0ZXJpc3RpY3MuIFRoZXNlIHZhcmlhYmxlcyBjYW4gYmUgY2F0ZWdvcml6ZWQgYXMgZm9sbG93czoNCg0KKk51bWVyaWNhbCBWYXJpYWJsZXM6Kg0KDQotIHdhZ2U6IFRoZSB3YWdlIG9mIHRoZSBpbmRpdmlkdWFsLg0KDQotIGVkdWNhdGlvbjogVGhlIG51bWJlciBvZiB5ZWFycyBvZiBlZHVjYXRpb24uDQoNCi0gZXhwZXJpZW5jZTogVGhlIG51bWJlciBvZiB5ZWFycyBvZiB3b3JrIGV4cGVyaWVuY2UuDQoNCi0gYWdlOiBUaGUgYWdlIG9mIHRoZSBpbmRpdmlkdWFsLg0KDQoqQ2F0ZWdvcmljYWwgVmFyaWFibGVzOioNCg0KLSBldGhuaWNpdHk6IFRoZSBldGhuaWNpdHkgb2YgdGhlIGluZGl2aWR1YWwuDQoNCi0gcmVnaW9uOiBUaGUgcmVnaW9uIHdoZXJlIHRoZSBpbmRpdmlkdWFsIGxpdmVzLg0KDQotIGdlbmRlcjogVGhlIGdlbmRlciBvZiB0aGUgaW5kaXZpZHVhbC4NCg0KLSBvY2N1cGF0aW9uOiBUaGUgb2NjdXBhdGlvbiBvZiB0aGUgaW5kaXZpZHVhbC4NCg0KLSBzZWN0b3I6IFRoZSBzZWN0b3Igb2YgZW1wbG95bWVudC4NCg0KLSB1bmlvbjogVW5pb24gbWVtYmVyc2hpcCBzdGF0dXMuDQoNCi0gbWFycmllZDogTWFyaXRhbCBzdGF0dXMuDQoNCg0KDQpgYGB7cn0NCnByaW50KGNvbFN1bXMoaXMubmEoQ1BTKSkpDQpgYGANClRoZXJlJ3Mgbm8gbWlzc2luZyB2YWx1ZXMgYWNyb3NzIHZhcmlhYmxlcywgc28gd2UgY2FuIHByb2NlZWQgd2l0aCB0aGUgYW5hbHlzaXMuDQoNCkxldCdzIHNlZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0KbnVtZXJpY2FsX3ZhcnMgPC0gYygid2FnZSIsICJlZHVjYXRpb24iLCAiZXhwZXJpZW5jZSIsICJhZ2UiKQ0Kc3VtbWFyeShDUFNbbnVtZXJpY2FsX3ZhcnNdKQ0KcHJpbnQocGFzdGUoIlNhbXBsZSBzaXplOiIsIG5yb3coQ1BTKSkpDQpgYGANCg0KV2UgY2FuIG9ic2VydmUgdGhhdCB0aGUgZGF0YXNldCBpbmNsdWRlcyA1MzQgYWR1bHQgcGVvcGxlIG9mIGFnZSByYW5naW5nIGZyb20gMTggdG8gNjQgeWVhcnMsIHdpdGggMiB0byAxOCB5ZWFycyBvZiBlZHVjYXRpb24gYW5kIDAgdG8gNTUgeWVhcnMgb2Ygd29yayBleHBlcmllbmNlLCBzbyB0aGUgdmFyaWFiaWxpdHkgYW1vbmcgdGhlIG9ic2VydmVkIHNhbXBsZSBvZiBpbmRpdmlkdWFscyBpcyBxdWl0ZSBsYXJnZS4gVGhlIHNhbWUgYXBwbGllcyB0byB3YWdlczogdGhleSByYW5nZSBmcm9tIDEgdG8gNDQuNSBkb2xsYXJzIHBlciBob3VyLg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ2lyYXBoIikNCmxpYnJhcnkoZ2dpcmFwaCkNCmxpYnJhcnkocGxvdGx5KQ0KDQpDUFNfbG9uZyA8LSBDUFMgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gYyh3YWdlLCBlZHVjYXRpb24sIGV4cGVyaWVuY2UsIGFnZSksIG5hbWVzX3RvID0gInZhcmlhYmxlX25hbWUiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKQ0KDQpxdWFudGlsZXMgPC0gQ1BTX2xvbmcgJT4lDQogIGdyb3VwX2J5KHZhcmlhYmxlX25hbWUpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgUTEgPSBxdWFudGlsZSh2YWx1ZSwgMC4yNSksDQogICAgTWVkaWFuID0gcXVhbnRpbGUodmFsdWUsIDAuNTApLA0KICAgIFEzID0gcXVhbnRpbGUodmFsdWUsIDAuNzUpDQogICkNCg0KZGVuc2l0eV9wbG90cyA8LSBnZ3Bsb3QoZGF0YSA9IENQU19sb25nLCBhZXMoeCA9IHZhbHVlKSkgKw0KICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJzdGVlbGJsdWUiLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC43KSArDQogIGZhY2V0X3dyYXAofiB2YXJpYWJsZV9uYW1lLCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgTnVtZXJpY2FsIFZhcmlhYmxlcyIsIHggPSBOVUxMLCB5ID0gIkZyZXF1ZW5jeSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgZ2VvbV92bGluZShkYXRhID0gcXVhbnRpbGVzLCBhZXMoeGludGVyY2VwdCA9IFExKSwgY29sb3IgPSAiYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAwLjUpICsNCiAgZ2VvbV92bGluZShkYXRhID0gcXVhbnRpbGVzLCBhZXMoeGludGVyY2VwdCA9IE1lZGlhbiksIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArDQogIGdlb21fdmxpbmUoZGF0YSA9IHF1YW50aWxlcywgYWVzKHhpbnRlcmNlcHQgPSBRMyksIGNvbG9yID0gImJsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMC41KQ0KDQpnZ3Bsb3RseShkZW5zaXR5X3Bsb3RzLCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQ0KYGBgDQpBcyB3ZSBjYW4gc2VlIGZyb20gdGhlIHBsb3RzLCB0aGUgZGlzdHJpYnV0aW9uIG9mIHdhZ2VzLCBleHBlcmllbmNlIGFuZCBhZ2UgaXMgcmlnaHQtc2tld2VkLCBtZWFuaW5nIHRoYXQgbW9zdCBpbmRpdmlkdWFscyBoYXZlIGxvd2VyIHZhbHVlcyBmb3IgdGhlc2UgdmFyaWFibGVzLiBUaGUgZGlzdHJpYnV0aW9uIG9mIGVkdWNhdGlvbiBpcyBtb3JlIGV2ZW5seSBzcHJlYWQsIGhhdmluZyBtZWRpYW4gdmFsdWUgYXQgYXJvdW5kIDEyIHllYXJzIG9mIGVkdWNhdGlvbi4NCg0KU2luY2UgdGhlIHZhcmlhYmxlcyBhcmUgc2tld2VkLCB0aGVyZSBtaWdodCBiZSBzb21lIG91dGxpZXJzLiBMZXQncyBoYXZlIGEgbG9vayBhdCB0aGUgYm94cGxvdHMgdG8gc2VlIGlmIHRoZXJlIGFyZSBhbnkuDQoNCmBgYHtyfQ0KYm94cGxvdHMgPC0gZ2dwbG90KENQU19sb25nLCBhZXMoeCA9IHZhcmlhYmxlX25hbWUsIHkgPSB2YWx1ZSkpICsNCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAic3RlZWxibHVlIiwgY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNykgKw0KICBmYWNldF93cmFwKH4gdmFyaWFibGVfbmFtZSwgc2NhbGVzID0gImZyZWUiLCBuY29sID0gNCkgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBOdW1lcmljYWwgVmFyaWFibGVzIiwgeCA9IE5VTEwsIHkgPSAiVmFsdWUiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpnZ3Bsb3RseShib3hwbG90cywgd2lkdGggPSA4MDAsIGhlaWdodCA9IDYwMCkNCmBgYA0KDQpUaGVyZSBhcmUgdmlzaWJsZSBvdXRsaWVycyBpbiB0aGUgZGF0YToNCg0KLSBFZHVjYXRpb246IHRoZXJlIGFyZSBwZW9wbGUgd2hvIGhhdmUgbGVzcyB0aGFuIDggeWVhcnMgb2YgZWR1Y2F0aW9uLCB3aGljaCBwcm9iYWJseSBtZWFucyB0aGV5IGZpbmlzaGVkIG9ubHkgYSBwYXJ0IG9mIHByaW1hcnkgZWR1Y2F0aW9uLiBUaGUgbnVtYmVyIG9mIHN1Y2ggb2JzZXJ2YXRpb25zIGlzIG5vdCBoaWdoIGFuZCB3ZSBhc3N1bWUgdGhhdCB0aGVzZSBhcmUgdmFsaWQgZGF0YSBwb2ludHMsIHNpbmNlIGEgc21hbGwgZnJhY3Rpb24gb2YgcG9wdWxhdGlvbiBpbmRlZWQgbWlnaHQgbm90IGZpbmlzaCBzY2hvb2wuDQoNCi0gRXhwZXJpZW5jZTogdGhlcmUgYXJlIGEgY291cGxlIG9mIG9ic2VydmF0aW9ucyB3aXRoIG1vcmUgdGhhbiA0OSB5ZWFycyBvZiB3b3JrIGV4cGVyaWVuY2UgKHdoaWNoIGlzIHRoZSA3NSUgcXVhbnRpbGUgb2YgdGhlIGRhdGEpLiBBIG9sZGVyIGZyYWN0aW9uIG9mIHBvcHVsYXRpb24gY291bGQgaW4gZmFjdCBzdGFydCB0aGVpciBjYXJlZXJzIGVhcmx5IGFuZCBoYXZlIGEgbG9uZyB3b3JrIGV4cGVyaWVuY2UsIHNvIHdlIGRvbid0IGNvbnNpZGVyIHRoZXNlIG9ic2VydmF0aW9ucyBwcm9ibGVtYXRpYyBvdXRsaWVycywgZ2l2ZW4gdGhhdCB0aGUgbWF4aW11bSBhZ2Ugd2l0aGluIHRoZSBzYW1wbGUgaXMgNjQgeWVhcnMuDQoNCi0gV2FnZTogdGhlcmUgYXJlIGEgZmV3IG9ic2VydmF0aW9ucyB3aXRoIHdhZ2VzIGhpZ2hlciB0aGFuIDMwIGRvbGxhcnMgcGVyIGhvdXIuIFRoZSB3YWdlIGRpc3RyaWJ1dGlvbiBub3JtYWxseSBpcyByaWdodC1za2V3ZWQsIHNvIHRoZXNlIG9ic2VydmF0aW9ucyBhcmUgbm90IHByb2JsZW1hdGljIG91dGxpZXJzLCBidXQgcmF0aGVyIGEgbG93IG51bWJlciBvZiBoaWdoIGVhcm5lcnMuDQoNCg0KIyMgQ29ycmVsYXRpb25zIGJldHdlZW4gd2FnZXMsIGVkdWNhdGlvbiwgZXhwZXJpZW5jZSwgYW5kIGFnZQ0KDQpMZXQncyBub3cgb2JzZXJ2ZSB0aGUgY29yZWxsYXRpb24gYmV0d2VlbiB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlcyBvZiBpbnRlcmVzdC4NCg0KYGBge3J9DQpudW1lcmljYWxfdmFyc19kZiA8LSBDUFNbbnVtZXJpY2FsX3ZhcnNdDQoNCmNvcnJlbGF0aW9uX21hdHJpeCA8LSBjb3IobnVtZXJpY2FsX3ZhcnNfZGYpDQoNCmNvcnJlbGF0aW9uX21hdHJpeF9sb25nIDwtIGFzLmRhdGEuZnJhbWUoY29ycmVsYXRpb25fbWF0cml4KSAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJWYXIxIikgJT4lDQogIGdhdGhlcihrZXkgPSAiVmFyMiIsIHZhbHVlID0gInZhbHVlIiwgLVZhcjEpICU+JQ0KICBmaWx0ZXIoVmFyMSAhPSBWYXIyKSAlPiUNCiAgZmlsdGVyKCFkdXBsaWNhdGVkKHBhc3RlKHBtaW4oVmFyMSwgVmFyMiksIHBtYXgoVmFyMSwgVmFyMikpKSkNCg0KDQpwcmludChjb3JyZWxhdGlvbl9tYXRyaXhfbG9uZykNCmBgYA0KVGhlIGluZGVwZW5kZW50IHZhcmlhYmxlIG9mIGludGVyZXN0IC0gV2FnZSAtIGlzIHBvc2l0aXZlbHkgY29ycmVsYXRlZCB3aXRoIG90aGVyIHRocmVlIG51bWVyaWNhbCB2YXJpYWJsZXMgd2l0aCB0aGUgc3Ryb25nZXN0IGNvcnJlbGF0aW9uIGZvciBlZHVjYXRpb24gKH4wLjQpLiBTdXJwcmlzaW5nbHksIHllYXJzIG9mIGV4cGVyaWVuY2UgYW5kIGFnZSBhbmQgbm90IHN0cm9uZ2x5IGNvcnJlbGF0ZWQgd2l0aCBXYWdlLg0KDQpUaGUgc3Ryb25nZXN0IHBvc2l0aXZlIGNvcnJlbGF0aW9uIGlzIG9ic2VydmFibGUgYmV0d2VlbiBhZ2UgYW5kIHdvcmsgZXhwZXJpZW5jZSwgd2hpY2ggaXMgbm90IHN1cHJpc2luZyBpZiB3ZSBhc3N1bWUgdGhhdCBhbG1vc3QgYWxsIGluZGl2aWR1YWxzIHdlcmUgd29ya2luZyB0aHJvdWdob3V0IHRoZWlyIGxpdmVzLg0KDQpXaGF0IGlzIGFsc28gaW50ZXJlc3RpbmcgaXMgdGhhdCBlZHVjYXRpb24gaXMgbm90IHN0cm9uZ2x5IGNvcnJlbGF0ZWQgd2l0aCBleHBlcmllbmNlLCB3aGljaCBtaWdodCBpbmRpY2F0ZSB0aGF0IHBlb3BsZSB3aXRoIGhpZ2hlciBlZHVjYXRpb24gbGV2ZWxzIG1pZ2h0IG5vdCBuZWNlc3NhcmlseSBoYXZlIG1vcmUgd29yayBleHBlcmllbmNlIChpdCBtYWtlcyBzZW5zZSBpZiB3ZSBhc3N1bWUgdGhhdCBpbmRpdmlkdWFscyB3aG8gd2VyZSBnZXR0aW5nIHRoZWlyIGVkdWNhdGlvbiBmb3IgYSBsb25nZXIgdGltZSwgbWlnaHQgc3RhcnQgd29ya2luZyBsYXRlcikuDQoNCkxldCdzIGdyYXBoaWNhbGx5IHJlcHJlc2VudCB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHdhZ2UgYW5kIGVkdWNhdGlvbiwgZXhwZXJpZW5jZSwgYW5kIGFnZS4NCg0KV2UgY2hvc2UgdG8gYXBwbHkgdGhlIExvZXNzIHNtb290aGluZyBtZXRob2QgdG8gdGhpcyBwbG90IGJlY2F1c2UgaXQgdGhpcyBtZXRob2QgaW4gbm9uLXBlcmFtZXRyaWMgYW5kIGRvZXMgbm90IGFzc3VtZSBhIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdmFyaWFibGVzLiBJbnN0ZWFkLCBMb2VzcyBmaXRzIGEgc21vb3RoZWQgY3VydmUgdGhhdCBiZXN0IGRlc2NyaWJlcyB0aGUgcmVsYXRpb25zaGlwLCBhbGxvd2luZyB1cyB0byBleHBsb3JlIHBvdGVudGlhbCBub24tbGluZWFyIHBhdHRlcm5zIGluIHRoZSBkYXRhIG1vcmUgY2xvc2VseS4gVGhpcyBhcHByb2FjaCBpcyBwYXJ0aWN1bGFybHkgc3VpdGFibGUgc2luY2Ugd2UgYXJlIHVuY2VydGFpbiBhYm91dCB0aGUgZXhhY3QgdHJlbmQgb2YgdGhlIHJlbGF0aW9uc2hpcC4NCg0KQWRkaXRpb25hbGx5LCB0aGUgZGF0YXNldCdzIHJlbGF0aXZlbHkgc21hbGwgc2l6ZSBtZWFucyB0aGF0IGluZGl2aWR1YWwgZGF0YSBwb2ludHMgY2FuIGhhdmUgYSBzaWduaWZpY2FudCBpbXBhY3QuIEJ5IHVzaW5nIExvZXNzLCB3aGljaCBpcyBzZW5zaXRpdmUgdG8gb3V0bGllcnMgYW5kIGxvY2FsIHZhcmlhdGlvbnMsIHdlIGNhbiBiZXR0ZXIgdW5kZXJzdGFuZCBob3cgdGhlc2UgZmFjdG9ycyBpbmZsdWVuY2UgdGhlIG92ZXJhbGwgdHJlbmQgYmV0d2VlbiB3YWdlIGFuZCBlZHVjYXRpb24uDQoNCmBgYHtyfQ0Kc2NhdHRlcnBsb3RfZWR1Y2F0aW9uIDwtIGdncGxvdChDUFMsIGFlcyh4ID0gZWR1Y2F0aW9uLCB5ID0gd2FnZSkpICsNCiAgZ2VvbV9wb2ludChzaXplPTEpICsNCiBnZW9tX3Ntb290aChtZXRob2Q9ImF1dG8iLCBzZT1UUlVFLCBmdWxscmFuZ2U9RkFMU0UsIGxldmVsPTAuOTUpICsNCiAgbGFicyh0aXRsZSA9ICJFZHVjYXRpb24gQW5kIFdhZ2UgUmVsYXRpb25zaGlwIiwNCiAgICAgICB4ID0gIlllYXJzIG9mIEVkdWNhdGlvbiIsDQogICAgICAgeSA9ICJXYWdlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90bHkoc2NhdHRlcnBsb3RfZWR1Y2F0aW9uLCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQ0KYGBgDQoNCklmIHdlIGNvbnNpZGVyIHRoZSBzY2F0dGVycGxvdCBvZiB3YWdlIGFuZCBlZHVjYXRpb24sIHdlIGNhbiBvYnNlcnZlIGEgcG9zaXRpdmUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMsIHRoYXQgd2FzIHByZXZpb3VzbHkgb2JzZXJ2ZWQgaW4gdGhlIGNvcnJlbGF0aW9uIG1hdHJpeC4gRnVydGhlcm1vcmUsIHRoZSB0cmVuZCBpcyByYXRoZXIgY29uc2lzdGVudCBhbmQgdGhlIHZhcmlhYmlsaXR5IGlzIG5vdCB0b28gaGlnaCwgd2hpY2ggaW5kaWNhdGVzIHRoYXQgdGhlIHJlbGF0aW9uc2hpcCBtaWdodCBiZSBxdWl0ZSBzdHJvbmcuIFRoZSBjb25zaXN0ZW50bHkgaW5jcmVhc2luZyB1cHdhcmQgc2xvcGUgbWlnaHQgc2lnbmFsIHRoYXQgdGhlcmUgaXMgYWxzbyBubyBkaW1pc2hpbmcgcmV0dXJucyB0byBtb3JlIHllYXJzIG9mIGVkdWNhdGlvbnMsIGF0IGxlYXN0IGluIHRoZSBzZWN0b3JzIGFuZCB0aGUgb2NjdXBhdGlvbnMgdGhhdCB3ZSBvYnNlcnZlIGluIHRoaXMgZGF0YXNldC4gDQoNCmBgYHtyfQ0Kc2NhdHRlcnBsb3RfZXhwZXJpZW5jZSA8LSBnZ3Bsb3QoQ1BTLCBhZXMoeCA9IGV4cGVyaWVuY2UsIHkgPSB3YWdlKSkgKw0KICBnZW9tX3BvaW50KHNpemU9MSkgKw0KIGdlb21fc21vb3RoKG1ldGhvZD0iYXV0byIsIHNlPVRSVUUsIGZ1bGxyYW5nZT1GQUxTRSwgbGV2ZWw9MC45NSkgKw0KICBsYWJzKHRpdGxlID0gIkV4cGVyaWVuY2UgQW5kIFdhZ2UgUmVsYXRpb25zaGlwIiwNCiAgICAgICB4ID0gIlllYXJzIG9mIEV4cGVyaWVuY2UiLA0KICAgICAgIHkgPSAiV2FnZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdncGxvdGx5KHNjYXR0ZXJwbG90X2V4cGVyaWVuY2UsIHdpZHRoID0gODAwLCBoZWlnaHQgPSA2MDApDQpgYGANCg0KQ29tcGFyZWQgdG8gZWR1Y2F0aW9uLCB3b3JrIGV4cGVyaWVuY2UgZG9lcyBub3Qgc2hvdyBhIGNsZWFyIHJlbGF0aW9uc2hpcCB3aXRoIHdhZ2U6IHZhcmlhYmlsaXR5IGlzIHNvIGhpZ2ggdGhhdCBpdCBtYWtlcyB0aGUgZ3JhcGggdmlzdWFsbHkgbm9pc3kgYW5kIHdpdGhvdXQgYSBjbGVhciBkaXN0aW5ndWlzaGFibGUgdHJlbmQuIFRoaXMgaXMgY29uc2lzdGVudCB3aXRoIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXgsIHdoaWNoIHNob3dlZCBhIHdlYWtlciBjb3JyZWxhdGlvbiBiZXR3ZWVuIHdhZ2UgYW5kIGV4cGVyaWVuY2UgY29tcGFyZWQgdG8gZWR1Y2F0aW9uLg0KDQpTcGVha2luZyBvZiBhIHRyZW5kIGluIHRoZSBmaXR0ZWQgbGluZSAtIHRoZXJlIGlzIGFuIHVwd2FyZCBzbG9wZSBmcm9tIDAgdG8gfjEyIHllYXJzIG9mIGV4cGVyaWVuY2VzIHdoaWNoIGlzIGZvbGx3ZWQgYnkgdGhlIGZsYXQgbGluZS4gSXQgbWlnaHQgc2lnbmFsIHRoYXQgdGhlIHBvc2l0aXZlIGxpbmVhcml0eSBvZiB0aGUgcmVsYXRpb25zaGlwIGlzIG5vdCBjb25zdGFudCBhY3Jvc3MgdGhlIHdob2xlIGNhcmVlciBhbmQgaXMgaGlnaGVyIGluIGZpcnN0IDEwLTE1IHllYXJzIG9mIHdvcmsgZXhwZXJpZW5jZSwgb24gYXZlcmFnZS4gVGhlcmUncyBhbHNvIGEgc2lnbmlmaWNhbnQgZHJvcCBpbiB3YWdlIGZvciBpbmRpdmlkdWFscyB3aXRoIG1vcmUgdGhhbiB+NDAgeWVhcnMgb2YgZXhwZXJpZW5jZSwgYnV0IHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGFyZSByZWxhdGl2ZWx5IGxvdyBpbiB0aGlzIGFnZSBhZ3JvdXAgYW5kIHRoZSB2YXJpYWJpbGl0eSBpcyBzbyBoaWdoIHRoYXQgaXQgZG9lcyBub3QgYWxsb3cgdG8gbWFrZSBhbnkgcHJlbGltaW5hcnkgY29uY2x1c2lvbnMuDQoNClNpbmNlIGFnZSBpcyBoaWdobHkgY29ycmVsYXRlZCB3aXRoIGV4cGVyaWVuY2UsIHdlIGNhbiBleHBlY3Qgc2ltaWxhciByZXN1bHRzIGZvciB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gd2FnZSBhbmQgYWdlIG9uIGF2ZXJhZ2UsIGJ1dCB0aGUgZXhhY3QgdHJlbmRzIG1pZ2h0IGRpZmZlciwgc28gaXQgbWlnaHQgYmUgdXNlZnVsIHRvIHBsb3QgaXQgYXMgd2VsbDoNCg0KYGBge3J9DQpzY2F0dGVycGxvdF9hZ2UgPC0gZ2dwbG90KENQUywgYWVzKHggPSBhZ2UsIHkgPSB3YWdlKSkgKw0KICBnZW9tX3BvaW50KHNpemU9MSkgKw0KIGdlb21fc21vb3RoKG1ldGhvZD0iYXV0byIsIHNlPVRSVUUsIGZ1bGxyYW5nZT1GQUxTRSwgbGV2ZWw9MC45NSkgKw0KICBsYWJzKHRpdGxlID0gIkV4cGVyaWVuY2UgQW5kIFdhZ2UgUmVsYXRpb25zaGlwIiwNCiAgICAgICB4ID0gIkFnZSIsDQogICAgICAgeSA9ICJXYWdlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90bHkoc2NhdHRlcnBsb3RfYWdlLCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQ0KYGBgDQpBcyB3aXRoIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB3YWdlIGFuZCBleHBlcmllbmNlLCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gd2FnZSBhbmQgYWdlIGlzIG5vdCBleGFjdGx5IGxpbmVhcmx5IHBvc2l0aXZlLiBUaGUgZml0dGVkIGxpbmUgc2hvd3MgYSBzbGlnaHQgdXB3YXJkIHNsb3BlIGZyb20gMTggdG8gMzUgeWVhcnMgb2YgYWdlLCB3aGljaCBpcyBmb2xsb3dlZCBieSBhIHN0YWduYXRpb24uIEl0IG1pZ2h0IGluZGljYXRlIHRoYXQgd2FnZSBnYWlucyBhcmUgaGlnaGVyIGluIGZpcnN0IHllYXJzIG9mIGNhcmVlciwgYnV0IHRoZW4gdGhlIHdhZ2UgZG9lcyBub3QgZ3JvdyBvciBldmVuIGRlY3JlYXNlIGEgYml0LiBJdCBpcyBhbHNvIGNvbnNpc3RlbnQgd2l0aCB0aGUgdHJlbmQgdGhhdCB3ZSBvYnNlcnZlZCBiZXR3ZWVuIHdhZ2UgYW5kIHllYXJzIG9mIGV4cGVyaWVuY2UuDQoNCiMjIERpc3RyaWJ1dGlvbiBvZiBXYWdlcyBBY3Jvc3MgRGlmZmVyZW50IFNvY2lhbCBHcm91cHMNCg0KTGV0J3MgaGF2ZSBhIGNsb3NlciBsb29rIGF0IHRoZSBxdWFsaXRhdGl2ZSBjYXRlZ29yaWVzIHRoYXQgd2UgaGF2ZSBpbiB0aGlzIGRhdGFzZXQuDQoNCmBgYHtyfQ0KY2F0ZWdvcmljYWxfdmFycyA8LSBjKCJldGhuaWNpdHkiLCAicmVnaW9uIiwgImdlbmRlciIsICJvY2N1cGF0aW9uIiwgInNlY3RvciIsICJ1bmlvbiIsICJtYXJyaWVkIikNCg0KZm9yICh2YXIgaW4gY2F0ZWdvcmljYWxfdmFycykgew0KICBjYXQoIkNhdGVnb3JpZXMgaW4iLCB2YXIsICI6XG4iKQ0KICBwcmludCh1bmlxdWUoQ1BTW1t2YXJdXSkpDQogIGNhdCgiXG4iKQ0KfQ0KYGBgDQpJdCBtaWdodCBiZSBpbnRlcmVzdGluZyB0byBvYnNlcnZlIGRpZmZlcmVuY2VzIGluIHdhZ2UgZGlzdHJpYnV0aW9uIGFjcm9zcyBkaWZmZXJlbnQgc29jaWFsIGdyb3VwcyBzZXBhcmF0ZWx5Lg0KDQoqKlNlY3RvcnMgYW5kIE9jY3VwYXRpb25zKioNCg0KTGV0J3Mgc3RhcnQgd2l0aCBwcm9mZXNzaW9uYWwgb2NjdXBhdGlvbnMgYW5kIHNlY3RvcnMuDQoNCldlIHN1c3BlY3QgdGhhdCB0aGVyZSBpcyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4gd2FnZXMgYW1vbmcgaW5kaXZpZHVhbHMgaW4gZGlmZmVyZW50IG9jY3VwYXRpb25zIGFuZCBpbmR1c3RyaWVzLCBzaW5jZSBzb21lIHNlY3RvcnMgYW5kIG9jY3VwYXRpb25zIHJlcXVpcmUgbW9yZSBhZHZhbmNlZCBhbmQvb3Igc3BlY2lhbGl6ZWQgc2tpbGxzIHRoYW4gb3RoZXJzLg0KDQpgYGB7cn0NCndhZ2VfYnlfc2VjdG9yIDwtIGdncGxvdChDUFMsIGFlcyh4ID0gc2VjdG9yLCB5ID0gd2FnZSkpICsNCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAic3RlZWxibHVlIiwgY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNykgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBXYWdlcyBBY3Jvc3MgU2VjdG9ycyIsDQogICAgICAgeCA9ICJTZWN0b3IiLA0KICAgICAgIHkgPSAiV2FnZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdncGxvdGx5KHdhZ2VfYnlfc2VjdG9yLCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQ0KYGBgDQpNZWRpYW4gYW5kIGludGVycXVhcnRpbGUgcmFuZ2VzIGFyZSBxdWl0ZSBjbG9zZSBhY3Jvc3Mgc2VjdG9ycy4gQXMgaXQgY291bGQgaGF2ZSBiZWVuIGV4cGVjdGVkLCAib3RoZXIiIGNhdGVnb3J5ICh3aGljaCBpbmNsdWRlcyBhbGwgZGF0YSBwb2ludHMgb3V0c2lkZSBvZiBjb25zdHJ1Y3Rpb24gYW5kIG1hbnVmYWN0dXJpbmcpIGhhdmUgdGhlIGxhcmdlc3QgYW5kIGJpZ2dlc3Qgb3V0bGllcnMsIGJ1dCB0aGUgbG93ZXN0IG1lZGlhbi4gQ29uc3RydWN0aW9uIHNlY3RvciBoYXMgdGhlIGhpZ2hlc3QgbWVkaWFuIHdhZ2UgYW5kIHRoZSBsb3dlc3QgdmFyaWFiaWxpdHkgaW4gd2FnZXMuDQoNCmBgYHtyfQ0Kc3VtbWFyeV9zZWN0b3JzIDwtIENQUyAlPiUNCiAgZ3JvdXBfYnkoc2VjdG9yKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIE1lYW4gPSBtZWFuKHdhZ2UpLA0KICAgIE1lZGlhbiA9IG1lZGlhbih3YWdlKSwNCiAgICBTRCA9IHNkKHdhZ2UpLA0KICAgIElRUiA9IElRUih3YWdlLCBuYS5ybSA9IFRSVUUpLA0KICAgIENvdW50ID0gbigpDQogICkNCg0Kc3VtbWFyeV9zZWN0b3JzDQpgYGANCkRlc3BpdGUgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGFjcm9zcyBzZWN0b3JzICgib3RoZXIiIGNhdGVnb3J5IGlzIHNpZ25pZmljYW50bHkgbGFyZ2VyIGluIG51bWJlciBvZiBvYnNlcnZhdGlvbnMpLCB0aGUgc2VjdG9ycyBhcHBlYXIgdG8gYmUgY29tcGFyYWJsZSBpbiB0ZXJtcyBvZiBtZWFuIGFuZCBtZWRpYW4uDQoNClRoZSBzdGFuZGFyZCBkZXZpYXRpb24gaXMgdGhlIGhpZ2hlc3QgaW4gdGhlICJvdGhlciIgY2F0ZWdvcnksIHdoaWNoIGlzIGNvbnNpc3RlbnQgd2l0aCB0aGUgYm94cGxvdC4gVGhlIGNvbnN0cnVjdGlvbiBzZWN0b3IgaGFzIHRoZSBsb3dlc3Qgc3RhbmRhcmQgZGV2aWF0aW9uLg0KDQpBY2NvcmRpbmcgdG8gdGhlIGNhbGN1bGF0ZWQgSVFScyBhY3Jvc3Mgc2VjdG9ycywgbWFudWZhY3R1cmluZyBoYXMgdGhlIGhpZ2hlc3QgdmFyaWFiaWxpdHkgb2YgdGhlIG1pZGRsZSA1MCUgb2YgdGhlIGRhdGEsIHdoaWNoIGlzIGNvbnNpc3RlbnQgd2l0aCB0aGUgYm94cGxvdC4gQ29uc3RydWN0aW9uIHNlY3RvciBoYXMgdGhlIGxvd2VzdCB2YXJpYWJpbGl0eSwgd2hpY2ggaXMgYWxzbyBjb25zaXN0ZW50IHdpdGggdGhlIGJveHBsb3QuDQoNCk92ZXJhbGwsIGRlc3BpcmUgcmF0aGVyIGNsb3NlZCBtZWFzdXJlcyBvZiBjZW50cmFsIHRlbmRlbmN5LCB0aGUgbWVhc3VyZXMgb2YgdmFyaWFiaWxpdHkgYXJlIHF1aXRlIGRpZmZlcmVudCBhY3Jvc3Mgc2VjdG9ycywgd2hpY2ggbWlnaHQgaW5kaWNhdGUgdGhhdCBzZWN0b3Igb2YgZW1wbG95ZW10biBpcyBhbiBpbXBvcnRhbnQgcHJlZGljdG9yIG9mIHdhZ2UgbGV2ZWwuDQoNCkl0IGlzIGFsc28gbGlrZWx5IHRvIGJlIGEgKmNvbW1vbiBjYXVzZSBjb25mb3VuZGVyKiBmb3IgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGVkdWNhdGlvbiBhbmQgd2FnZXMsIHNpbmNlIHBlb3BsZSB3b3JraW5nIGluIGRpZmZlcmVudCBzZWN0b3JzIG1pZ2h0IGhhdmUgZGlmZmVyZW50IGxldmVscyBvZiBlZHVjYXRpb24gYW5kIHdhZ2VzLiBpdCBtaWdodCBiZSBlc3BlY2lhbGx5IHRoZSBjYXNlIGZvciAib3RoZXIiIGNhdGVnb3J5IHNpbmNlIGl0IGluY2x1ZGVzIGEgd2lkZSB2YXJpZXR5IG9mIGluZHVzdHJpZXMuDQoNCg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoImZvcmNhdHMiKQ0KbGlicmFyeShmb3JjYXRzKQ0KbGlicmFyeShkcGx5cikNCg0KQ1BTIDwtIENQUyAlPiUNCiAgbXV0YXRlKG9jY3VwYXRpb24gPSBmY3RfcmVvcmRlcihvY2N1cGF0aW9uLCB3YWdlLCAuZnVuID0gbWVkaWFuKSkNCg0Kd2FnZV9ieV9vY2N1cGF0aW9uIDwtIGdncGxvdChDUFMsIGFlcyh4ID0gb2NjdXBhdGlvbiwgeSA9IHdhZ2UpKSArDQogIGdlb21fYm94cGxvdChmaWxsID0gInN0ZWVsYmx1ZSIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjcpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgV2FnZXMgQWNyb3NzIE9jY3VwYXRpb25zIiwNCiAgICAgICB4ID0gIk9jY3VwYXRpb24iLA0KICAgICAgIHkgPSAiV2FnZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdncGxvdGx5KHdhZ2VfYnlfb2NjdXBhdGlvbiwgd2lkdGggPSA4MDAsIGhlaWdodCA9IDYwMCkNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnlfb2NjdXBhdGlvbnMgPC0gQ1BTICU+JQ0KICBncm91cF9ieShvY2N1cGF0aW9uKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIE1lYW4gPSBtZWFuKHdhZ2UpLA0KICAgIE1lZGlhbiA9IG1lZGlhbih3YWdlKSwNCiAgICBTRCA9IHNkKHdhZ2UpLA0KICAgIElRUiA9IElRUih3YWdlLCBuYS5ybSA9IFRSVUUpLA0KICAgIENvdW50ID0gbigpDQogICkNCg0Kc3VtbWFyeV9vY2N1cGF0aW9ucw0KYGBgDQpIaWdoZXN0IG1lZGlhbiBhbmQgaW50ZXJxdWFydGlsZSByYW5nZXMgYXJlIG9ic2VydmFibGUgaW4gdGhlIE1hbmFnZW1lbnQgYW5kIFRlY2huaWNhbCBncm91cHMsIHdoaWNoIG1lYW5zIHRoZXkgaGF2ZSBoaWdoZXIgaG91cmx5IHdhZ2UgZm9yIGEgInR5cGljYWwiIHdvcmtlciBhcyB3ZWxsIGFzIGxhcmdlciB2YXJpYWJpbGl0eSBpbiB3YWdlcy4gVGhlc2UgdHdvIHR5cGVzIG9mIG9jY3VwYXRpb25zIG9mdGVuIHJlcXVpcmUgYWR2YW5jZWQgbGV2ZWwgZWR1Y2F0aW9uIGFuZCBzcGVjaWFsaXplZCBza2lsbHMsIHdoaWNoIGNhbiBleHBsYWluIHRoZSBoaWdoZXIgd2FnZXMuDQoNCldvcmtlciBhbmQgU2FsZXMgb2NjdXBhdGlvbnMgaGF2ZSBxdWl0ZSBsb25nIHRhaWxzIHdpdGggcmVsYXRpdmVseSBzbWFsbCBudW1iZXJzIG9mIHZlcnkgaGlnaCBlYXJuZXJzLiBTdWNjZXNzZnVsIHNhbGVzIHNwZWNpYWxpc3RzIGNhbiBoYXZlIGhpZ2ggaW5jb21lcyBmcm9tIGNvbW1pc3Npb25zLCB3aGlsZSBzb21lIGhpZ2hseSBxdWFsaWZpZWQgd29ya2VycyBjYW4gaGF2ZSBtdWNoIGhpZ2hlciBob3VybHkgd2FnZXMsIHRoYW4gYXZlcmFnZSB3b3JrZXJzIGR1ZSB0byB0aGVpciBoaWdoIGxldmVsIG9yIGhpZ2hseSBzcGVjaWFsaXplZCBza2lsbHMuDQoNCk92ZXJhbGwsIHRoZXJlIGlzIHF1aXRlIGhpZ2ggdmFyaWFiaWxpdHksIGF2ZXJhZ2UgYW5kIG1lZGlhbiBkaWZmZXJlbmNlcyBpbiB3YWdlcyBhY3Jvc3MgdGhlc2UgZ3JvdXBzLCB3aGljaCBtaWdodCBpbmRpY2F0ZSB0aGF0IG9jY3VwYXRpb24gaXMgYSB2ZXJ5IGltcG9ydGFudCBwcmVkaWN0b3Igb2Ygd2FnZSBsZXZlbC4gSXQgaXMgYWxzbyBsaWtlbHkgdG8gYmUgYSAqY29tbW9uIGNhdXNlIGNvbmZvdW5kZXIqIGZvciB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZWR1Y2F0aW9uIGFuZCB3YWdlcywgc2luY2UgcGVvcGxlIHdpdGggaGlnaGVyIGVkdWNhdGlvbiBsZXZlbHMgbWlnaHQgYmUgbW9yZSBsaWtlbHkgdG8gd29yayBpbiBoaWdoZXIgcGFpZCBzZWN0b3JzIGFuZCBvY2N1cGF0aW9ucyAoaXQgZXNwZWNpYWxseSBob2xkcyB0cnVlIGZvciBtYW5hZ2VtZW50IGFuZCB0ZWNobmljYWwgZ3JvdXBzIHRoYXQgbWlnaHQgcmVxdWlyZSBhZHZhbmNlZCBsZXZlbCBlZHVjYXRpb24pLg0KDQoqKkV0aG5pYyBncm91cHMqKg0KDQpMZXQncyBub3cgb2JzZXJ2ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHdhZ2VzIGFjcm9zcyBkaWZmZXJlbnQgZGVtb2dyYXBoaWMgZ3JvdXBzLg0KDQpgYGB7cn0NCkNQUyA8LSBDUFMgJT4lDQogIG11dGF0ZShldGhuaWNpdHkgPSBmY3RfcmVvcmRlcihldGhuaWNpdHksIHdhZ2UsIC5mdW4gPSBtZWRpYW4pKQ0KDQp3YWdlX2J5X2V0aG5pY2l0eSA8LSBnZ3Bsb3QoQ1BTLCBhZXMoeCA9IGV0aG5pY2l0eSwgeSA9IHdhZ2UpKSArDQogIGdlb21fYm94cGxvdChmaWxsID0gInN0ZWVsYmx1ZSIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjcpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgV2FnZXMgQWNyb3NzIEV0aG5pYyBHcm91cHMiLA0KICAgICAgIHggPSAiT2NjdXBhdGlvbiIsDQogICAgICAgeSA9ICJXYWdlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90bHkod2FnZV9ieV9ldGhuaWNpdHksIHdpZHRoID0gODAwLCBoZWlnaHQgPSA2MDApDQpgYGANCg0KYGBge3J9DQpzdW1tYXJ5X2V0aG5pY2l0eSA8LSBDUFMgJT4lDQogIGdyb3VwX2J5KGV0aG5pY2l0eSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBNZWFuID0gbWVhbih3YWdlKSwNCiAgICBNZWRpYW4gPSBtZWRpYW4od2FnZSksDQogICAgU0QgPSBzZCh3YWdlKSwNCiAgICBJUVIgPSBJUVIod2FnZSwgbmEucm0gPSBUUlVFKSwNCiAgICBDb3VudCA9IG4oKQ0KICApDQoNCnN1bW1hcnlfZXRobmljaXR5DQpgYGANCkNhdWNhc2lhbnMgaGF2ZSBib3RoIHRoZSBoaWdoZXN0IG1lZGlhbiB3YWdlIGFuZCB0aGUgaGlnaGVzdCB2YXJpYWJpbGl0eSBpbiB3YWdlcy4NCg0KSGlnaGVyIHZhcmlhYmlsaXR5IGNhbiBiZSBleHBsYWluZWQgYnkgdGhlIGZhY3QgdGhhdCB0aGUgbWFqb3JpdHkgb2YgdGhlIHNhbXBsZSBpcyBDYXVjYXNpYW4sIHNvIHRoZSBkaXN0cmlidXRpb24gb2Ygd2FnZXMgaXMgbW9yZSBzcHJlYWQgb3V0LiBBZGRpdGlvbmFsbHksIHRoZXJlIG1pZ2h0IHN0cnVjdHVyYWwgZGlmZmVyZW5jZXMgaW4gaW5jb21lcyBiZXR3ZWVuIGRpZmZlcmVudCBldGhuaWMgZ3JvdXBzLiBGb3IgaW5zdGFuY2UsIG5vbi13aGl0ZSBtaW5vcml0aWVzIGhhdmUgbG93ZXIgYWNjZXNzIHRvIGhpZ2hlciBwYWlkIHNlY3RvcnMgYW5kIG9jY3VwYXRpb25zLCB3aGljaCBjYW4gbGVhZCB0byBsb3dlciB3YWdlcy4NCg0KDQpgYGB7cn0NCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KDQpldGhuaWNpdHlfY29sb3JzIDwtIGMoImhpc3BhbmljIiA9ICJicm93bjQiLCAib3RoZXIiID0gImRhcmtnb2xkZW5yb2QiLCAiY2F1YyIgPSAiY2FkZXRibHVlIikNCg0KZXRobmljaXR5X29jY3VwYXRpb25fcGxvdCA8LSBnZ3Bsb3QoQ1BTLCBhZXMoeCA9IG9jY3VwYXRpb24sIGZpbGwgPSBldGhuaWNpdHkpKSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArDQogIGxhYnModGl0bGUgPSAiRXRobmljIENvbXBvc2l0aW9uIEFjcm9zcyBPY2N1cGF0aW9ucyIsDQogICAgICAgeCA9ICJPY2N1cGF0aW9uIiwNCiAgICAgICB5ID0gIlByb3BvcnRpb24iLA0KICAgICAgIGZpbGwgPSAiRXRobmljaXR5IikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGV0aG5pY2l0eV9jb2xvcnMpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCg0KZ2dwbG90bHkoZXRobmljaXR5X29jY3VwYXRpb25fcGxvdCkNCmBgYA0KDQpBcyB3ZSBjYW4gc2VlLCBhbW9uZyBvY2N1cGF0aW9uIGdyb3Vwcywgbm9uLWNhdXNhc2lhbnMnIHByb3BvcnRpb25zIGFyZSB0aGUgaGlnaGVzdCBpbiB0aGUgb2ZmaWNlLCB3b3JrZXIgYW5kIHNlcnZpY2VzIG9jY3VwYXRpb24gZ3JvdXBzLCB3aGljaCBhcmUgYXNzb2NpYXRlZCB3aXRoIGxvd2VyIHdhZ2VzLCBhcyB3ZSBzYXcgcHJldmlvdXNseS4NCiANCkdlbmVyYWxseSBzcGVha2luZywgZXRobmljIG9yaWdpbiBtaWdodCBiZSBhIHZlcnkgaW1wb3J0YW50IHByZWRpY3RvciBvZiBhIHdhZ2UgbGV2ZWwuIEFkZGl0aW9uYWxseSwgaXQgbWlnaHQgYWxzbyBhZmZlY3QgdGhlIGFjY2VzcyB0byBlZHVjYXRpb24sIHdoaWNoIG1lYW5zIHRoYXQgaXQgaXMgYSAqY29tbW9uIGNhdXNlIGNvbmZvdW5kZXIqLg0KDQoqKkdlbmRlcioqDQoNCkxldCdzIG9ic2VydmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB3YWdlcyBhY3Jvc3MgZ2VuZGVyLiBHZW5kZXIgZGlzcGFyaXR5IGlzIHdpZGVseSBkaXNjdXNzZWQgaW4gdGhlIGVjb25vbWljIGxpdGVyYXR1cmUsIHNvIGl0IG1pZ2h0IGJlIGludGVyZXN0aW5nIHRvIHNlZSBpZiB0aGVyZSBpcyBhIHdhZ2UgZ2FwIGJldHdlZW4gdHdvIGdlbmRlcnMgaW4gdGhpcyBkYXRhc2V0LiBXZSB3b3VsZCBleHBlY3QgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlLCBlc3BlY2lhbGx5IHNpbmNlIHRoZSBkYXRhIGNvbWVzIGZyb20gODAncyB3aGVuIGdlbmRlciBwYXkgZ2FwIHdhcyBldmVuIG1vcmUgcHJvbm91bmNlZC4NCg0KYGBge3J9DQp3YWdlX2J5X2dlbmRlciA8LSBnZ3Bsb3QoQ1BTLCBhZXMoeCA9IGdlbmRlciwgeSA9IHdhZ2UpKSArDQogIGdlb21fYm94cGxvdChmaWxsID0gInN0ZWVsYmx1ZSIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjcpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgV2FnZXMgQWNyb3NzIEdlbmRlciBHcm91cHMiLA0KICAgICAgIHggPSAiR2VuZGVyIiwNCiAgICAgICB5ID0gIldhZ2UiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpnZ3Bsb3RseSh3YWdlX2J5X2dlbmRlciwgd2lkdGggPSA4MDAsIGhlaWdodCA9IDYwMCkNCmBgYA0KDQpFdmVuIHRob3VnaCBib3RoIG1lZGlhbiBhbmQgaW50ZXJxdWFydGlsZSByYW5nZXMgYXJlIGhpZ2hlciBmb3IgbWFsZXMsIHRoZXJlIGlzIHF1aXRlIGEgbG90IG9mIG91dGxpZXJzIGluIHRoZSBmZW1hbGUgZ3JvdXBzLCBpbmRpY2F0aW5nIGEgc21hbGwgbnVtYmVyIG9mIGhpZ2ggZWFybmVycyBhbW9uZyBmZW1hbGVzLg0KDQpgYGB7cn0NCnN1bW1hcnlfZ2VuZGVyIDwtIENQUyAlPiUNCiAgZ3JvdXBfYnkoZ2VuZGVyKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIE1lYW4gPSBtZWFuKHdhZ2UpLA0KICAgIE1lZGlhbiA9IG1lZGlhbih3YWdlKSwNCiAgICBTRCA9IHNkKHdhZ2UpLA0KICAgIElRUiA9IElRUih3YWdlLCBuYS5ybSA9IFRSVUUpLA0KICAgIENvdW50ID0gbigpDQogICkNCg0Kc3VtbWFyeV9nZW5kZXINCmBgYA0KRGVzcGl0ZSBvdXRsaWVycyBtZW50aW9uZWQgZWFybGllciwgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBhbmQgaW50ZXJxdWFydGlsZSByYW5nZSBvZiB0aGUgbWFsZSBncm91cCBzaWduYWwgaGlnaGVyIHZhcmlhYmlsaXR5IGluIHdhZ2VzLg0KDQpBZ2Fpbiwgd2Ugd291bGQgZXhwZWN0IGdlbmRlciB0byBiZSBhICpjb21tb24gY2F1c2UgY29uZm91bmRlciogZm9yIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBlZHVjYXRpb24gYW5kIHdhZ2VzLCBiZWNhdXNlIGl0IGNhbiBhZmZlY3QgYm90aCB0aGUgYWNjZXNzIHRvIGVkdWNhdGlvbiwgbnVtYmVyIG9mIHllYXJzIG9mIGZvcm1hbCBlZHVjYXRpb24gKGR1ZSB0byBkaWZmZXJlbnQgc29jaWFsIG5vcm1zIGFuZCBleHBlY3RhdGlvbnMgYXBwbGllZCB0byBmZW1hbGVzKSBhbmQgdGhlIHdhZ2UgbGV2ZWwgKGR1ZSB0byBnbGFzcyBjZWlsaW5ncykuDQoNCipMb2NhdGlvbioNCg0KVGhlIGRhdGEgYWxzbyBhbGxvd3MgdG8gb2JzZXJ2ZSB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiByZWdpb25zIGluIHRlcm1zIG9mIHdhZ2VzLiBUaGUgZGF0YXNldCBpbmNsdWRlcyAyIGNhdGVnb3JpZXMgcmVsYXRlZCB0byBsb2NhdGlvbjogc291dGggYW5kIG90aGVyLiBXZSB3b3VsZCBleHBlY3QgdGhhdCB0aGVyZSBpcyBhIHdhZ2UgZ2FwIGJldHdlZW4gdGhlc2UgdHdvIHJlZ2lvbnMsIHNpbmNlIHRoZSBTb3V0aCBpcyBnZW5lcmFsbHkgY29uc2lkZXJlZCB0byBiZSBtb3JlIHJ1cmFsLCBsZXNzIGRldmVsb3BlZCwgcmVzdWx0aW5nIGluIGxvd2VyIGF2ZXJhZ2Ugd2FnZXMuDQoNCmBgYHtyfQ0Kd2FnZV9ieV9yZWdpb24gPC0gZ2dwbG90KENQUywgYWVzKHggPSByZWdpb24sIHkgPSB3YWdlKSkgKw0KICBnZW9tX2JveHBsb3QoZmlsbCA9ICJzdGVlbGJsdWUiLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC43KSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFdhZ2VzIEFjcm9zcyBSZWdpb25zIiwNCiAgICAgICB4ID0gIlJlZ2lvbiIsDQogICAgICAgeSA9ICJXYWdlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90bHkod2FnZV9ieV9yZWdpb24sIHdpZHRoID0gODAwLCBoZWlnaHQgPSA2MDApDQpgYGANCg0KSXQncyB2aXZpZGx5IHZpc2libGUgdGhhdCBib3RoIHRoZSBtZWRpYW4gYW5kIHRoZSByYW5nZSBhcmUgaGlnaGVyIGluIHRoZSAib3RoZXIiIGNhdGVnb3J5LCB3aGljaCBpcyBjb25zaXN0ZW50IHdpdGggdGhlIGFzc3VtcHRpb24gdGhhdCB0aGUgU291dGggaGFzIGxvd2VyIHdhZ2VzLg0KDQpgYGB7cn0NCnN1bW1hcnlfcmVnaW9uIDwtIENQUyAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIE1lYW4gPSBtZWFuKHdhZ2UpLA0KICAgIE1lZGlhbiA9IG1lZGlhbih3YWdlKSwNCiAgICBTRCA9IHNkKHdhZ2UpLA0KICAgIElRUiA9IElRUih3YWdlLCBuYS5ybSA9IFRSVUUpLA0KICAgIENvdW50ID0gbigpDQogICkNCg0Kc3VtbWFyeV9yZWdpb24NCmBgYA0KKlVuaW9uaXphdGlvbioNCg0KVW5pb25pemF0aW9uIGlzIGFub3RoZXIgaW1wb3J0YW50IGZhY3RvciB0aGF0IGNhbiBhZmZlY3Qgd2FnZXM6IHVuaW9uIG1lbWJlciBhcmUgcHJvdGVjdGVkIGJ5IHRoZSBjb2xsZWN0aXZlIGFncmVlbWVudHMgYW5kIHVzdWFsbHkgaGF2ZSBoaWdoZXIgd2FnZXMuDQoNCmBgYHtyfQ0Kd2FnZV9ieV91bmlvbiA8LSBnZ3Bsb3QoQ1BTLCBhZXMoeCA9IHVuaW9uLCB5ID0gd2FnZSkpICsNCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAic3RlZWxibHVlIiwgY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNykgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBXYWdlcyBBY3Jvc3MgVW5pb24gTWVtYmVyc2hpcHMiLA0KICAgICAgIHggPSAiVW5pb24gTWVtYmVyc2hpcCIsDQogICAgICAgeSA9ICJXYWdlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90bHkod2FnZV9ieV91bmlvbiwgd2lkdGggPSA4MDAsIGhlaWdodCA9IDYwMCkNCmBgYA0KDQoNCg0KIyMgQ2F1c2FsIEFuYWx5c2lzDQoNCkFmdGVyIHByZWxpbWluYXJ5IGNvcnJlbGF0aW9uIGFuZCBleHBsb3JhdG9yeSBhbmFseXNpcywgd2UgY2FuIG5vdyBwcm9jZWVkIHdpdGggdGhlIGh5cG90aGVzaXMgdGVzdGluZy4gRWR1Y2F0aW9uIHdhcyBmb3VuZCB0byBoYXZlIHRoZSBoaWdoZXN0IHBvc2l0aXZlIGNvcnJlbGF0aW9uIHdpdGggd2FnZXMgYW1vbmcgcXVhbnRpdGF0aXZlIHZhcmlhYmxlcyAoY29ycmVsYXRpb25zIGJldHdlZW4gd2FnZXMgYW5kIGFnZSBhbmQgZXhwZXJpZW5jZSB3ZXJlIHdlYWtlcikuIFF1YWxpdGF0aXZlIHZhcmlhYmxlcywgc3VjaCBhcyBvY2N1cGF0aW9uIGFuZCBzZWN0b3IsIGFsc28gc2hvd2VkIHNvbWUgZGlmZmVyZW5jZXMgaW4gd2FnZSBkaXN0cmlidXRpb24gYW5kIHRoZWlyIHJlbGF0aW9uc2hpcCB3aXRoIHdhZ2VzIG1pZ2h0IGJlIGludGVyZXN0aW5nIHRvIGV4cGxvcmUgZnVydGhlci4NCg0KVGhlIHNldCBvZiBoeXBvdGhlc2VzIGlzIGZvcm11bGF0ZWQgaW4gdGhlIGZvbGxvd2luZyB3YXk6DQoNCi0gKk51bGwgSHlwb3RoZXNpcyAoSOKCgCk6IEVkdWNhdGlvbiBoYXMgbm8gZWZmZWN0IG9uIHdhZ2VzLioNCi0gKkFsdGVybmF0aXZlIEh5cG90aGVzaXMgKEjigoEpOiBFZHVjYXRpb24gaGFzIGFuIGVmZmVjdCBvbiB3YWdlcy4qDQoNCg0KTGV0J3MgaGF2ZSBhIGxvb2sgYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBsZXZlbCBvZiBlZHVjYXRpb24sIGNvbnRyb2xsaW5nIGZvciBleHBlcmllbmNlIGFuZCBhZ2UsIGFuZCB3YWdlcy4NCg0KRmlyc3QsIHNpbmNlIGFnZSBhbmQgZXhwZXJpZW5jZSBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQsIHdlIHdpbGwgY2hlY2sgZm9yICptdWx0aWNvbGxpbmVhcml0eSogYmV0d2VlbiB0aGVzZSB0d28gdmFyaWFibGVzLCB1c2luZyAqVmFyaWFuY2UgSW5mbGF0aW9uIEZhY3RvciAoVklGKSouIExldCdzIGNyZWF0ZSBtb2RlbHMgd2l0aCBhbmQgd290aG91dCBjb2xsaW5lYXIgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGFuZCBjb21wYXJlIHRoZSByZXN1bHRzOg0KDQpgYGB7cn0NCiMgaW5zdGFsbC5wYWNrYWdlcygic3RhcmdhemVyIikNCmxpYnJhcnkoc3RhcmdhemVyKQ0KDQptb2RlbF9udW1lcmljYWxfMSA8LSBsbSh3YWdlIH4gZWR1Y2F0aW9uICsgZXhwZXJpZW5jZSArIGFnZSwgZGF0YSA9IENQUykNCm1vZGVsX251bWVyaWNhbF8yIDwtIGxtKHdhZ2UgfiBlZHVjYXRpb24gKyBleHBlcmllbmNlLCBkYXRhID0gQ1BTKQ0KbW9kZWxfbnVtZXJpY2FsXzMgPC0gbG0od2FnZSB+IGVkdWNhdGlvbiArIGFnZSwgZGF0YSA9IENQUykNCg0Kc3RhcmdhemVyKG1vZGVsX251bWVyaWNhbF8xLCBtb2RlbF9udW1lcmljYWxfMiwgbW9kZWxfbnVtZXJpY2FsXzMsDQogICAgICAgICAgdHlwZSA9ICJ0ZXh0IiwNCiAgICAgICAgICB0aXRsZSA9ICJXYWdlIFZzLiBFZHVjYXRpb24sIEV4cGVyaWVuY2UgJiBBZ2UiLA0KICAgICAgICAgIGFsaWduID0gVFJVRSwNCiAgICAgICAgICBjb2x1bW4ubGFiZWxzID0gYygiQWxsIG51bWVyaWNhbCIsICJ3L28gQWdlIiwgIncvbyBFeHBlcmllbmNlIikpDQpgYGANCkZpcnN0IG1vZGVsIGluY2x1ZGVzIGFsbCB0aHJlZSB2YXJpYWJsZXMsIHdoaWxlIHRoZSBzZWNvbmQgbW9kZWwgaW5jbHVkZXMgb25seSBlZHVjYXRpb24gYW5kIGV4cGVyaWVuY2UgKGV4Y2x1ZGluZyBhZ2UpLiBUaGUgdGhpcmQgbW9kZWwgaW5jbHVkZXMgb25seSBlZHVjYXRpb24gYW5kIGFnZSAoZXhjbHVkaW5nIGV4cGVyaWVuY2UpLg0KDQpGaXJzdCBtb2RlbCBkZW1vbnN0cmF0ZXMgaGlnaGx5IGluZmxhdGVkIHN0YW5kYXJkIGVycm9ycywgbW9zdCBsaWtlbHkgZHVlIHRvIG11bHRpY29sbGluZWFyaXR5IGJldHdlZW4gZXhwZXJpZW5jZSBhbmQgYWdlLg0KDQpCb3RoIDJuZCBhbmQgMnJkIG1vZGVscyBzaG93IGFuZCBzdGF0aXN0aWNhbCBhbmQgcXVpdGUgaGlnaCBjb3JyZWxhdGlvbiBiZXR3ZWVuIGVkdWNhdGlvbiBhbmQgd2FnZSwgd2hpbGUgdGhlIHJlc3BlY3RpdmUgY29lZmZpY2llbnRzIGZvciBpc29sYXRlZCBleHBlcmllbmNlIGFuZCBhZ2UgdmFyaWFibGVzIGFyZSBxdWl0ZSBzaW1pbGFyLiBpdCBpcyBpbmRlZWQgYSBzaWduIG9mIG11bHRpY29sbGluZWFyaXR5Lg0KDQpMZXQncyBzZWUgdGhlIFZhcmlhbmNlIEluZmxhdGlvbiBGYWN0b3IgdG8gY2hlY2sgdGhlIHBvd2VyIG9mIG11bHRpY29sbGluZWFyaXR5IGluIHRoZSAxc3QgbW9kZWw6DQoNCmBgYHtyfQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJjYXIiKQ0KbGlicmFyeShjYXIpDQoNCnZpZjEgPC0gdmlmKG1vZGVsX251bWVyaWNhbF8xKQ0KcHJpbnQodmlmMSkNCmBgYA0KVGhlIGNhbGN1bGF0ZWQgVklGIGlzIGV4dHJlbWVseSBoaWdoLCB3aGljaCBpbmRpY2F0ZXMgdGhhdCBtdWx0aWNvbGxpbmVhcml0eSBpcyBhIHNpZ25pZmljYW50IGlzc3VlIGluIHRoZSBtb2RlbC4NCg0KTGV0J3MgY2hlY2sgVklGIGZvciB0aGUgMm5kIGFuZCAzcmQgbW9kZWxzOg0KYGBge3J9DQp2aWYyIDwtIHZpZihtb2RlbF9udW1lcmljYWxfMikNCnByaW50KHZpZjIpDQpgYGANCmBgYHtyfQ0KdmlmMyA8LSB2aWYobW9kZWxfbnVtZXJpY2FsXzMpDQpwcmludCh2aWYzKQ0KYGBgDQpCb3RoIG1vZGVscyBoYXZlIFZJRiB2YWx1ZXMgfjEsIHdoaWNoIGlzIGEgc2lnbiBvZiBubyBtdWx0aWNvbGxpbmVhcml0eS4NCg0KTGV0J3Mgb2JzZXJ2ZSB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gd2FnZSBhbmQgc29jaW8tZWNvbm9taWMgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBpbmRpdmlkdWFscy4NCg0KYGBge3J9DQoNCm1vZGVsX2NhdGVnb3JpY2FsIDwtIGxtKHdhZ2UgfiBldGhuaWNpdHkgKyByZWdpb24gKyBnZW5kZXIgKyBvY2N1cGF0aW9uICsgc2VjdG9yIA0KKyB1bmlvbiArIG1hcnJpZWQsIGRhdGEgPSBDUFMpDQoNCnN0YXJnYXplcihtb2RlbF9jYXRlZ29yaWNhbCwgdHlwZSA9ICJ0ZXh0IiwgdGl0bGUgPSAiV2FnZSBWcy4gQ2F0ZWdvcmljYWwgdmFyaWFibGVzIiwgYWxpZ24gPSBUUlVFKQ0KYGBgDQpTaW5jZSBpdCBpcyBub3QgYW4gZW50aXJlIG1vZGVsIG9mIHRoZSByZWxhdGlvbnNoaXAgd2UgYXJlIGludGVyZXN0ZWQgaW4sIHdlIGNhbid0IG1ha2UgYW55IGNvbmNsdXNpb25zIGFib3V0IHRoZSBlZmZlY3Qgb2YgdGhlc2UgdmFyaWFibGVzIG9uIHdhZ2VzLiBIb3dldmVyLCBpdCBpcyB2YWx1YWJsZSB0byBtYWtlIHByZWxpbWluYXJ5IG9ic2VydmF0aW9ucyBvbiBob3cgdGhlc2UgY29tbW9uIGNhdXNlIGNvbmZvdW5kZXJzIGNvcnJlbGF0ZWQgd2l0aCB0aGUgd2FnZXMuDQoNCkFzIHdlIGNhbiBzZWU6DQoNCi0gQmVpbmcgYSBtYWxlIGlzIGluZGVlZCBxdWl0ZSBsaWtlbHkgdG8gYmUgYXNzb2NpYXRlZCB3aXRoIGhpZ2hlciB3YWdlcw0KDQotIEJlaW5nIG9mIGNhdWNhdXNpYW4gZXRobmljIG9yaWdpbiBjYW4gYmUgYXNzb2NpYXRlZCB3aXRoIGhpZ2hlciB3YWdlcw0KDQotIExpdmluZyBpbiB0aGUgU291dGggY2FuIGJlIGFzc29jaWF0ZWQgd2l0aCBsb3dlciB3YWdlcw0KDQotIEhhdmluZyBhbiBvY2N1cGF0aW9uIGluIHRoZSB0ZWNobmljYWwgYW5kIG1hbmFnZXJpYWwgcG9zaXRpb25zIGNhbiBiZSBhc3NvY2lhdGVkIHdpdGggaGlnaGVyIHdhZ2VzDQoNCi0gQmVpbmcgYSB1bmlvbiBtZW1iZXIgY2FuIGJlIGFzc29jaWF0ZWQgd2l0aCBoaWdoZXIgd2FnZXMNCg0KVGhpcyBjaGVjayBpcyBjb25zaXN0ZW50IHdpdGggdGhlIGFzc3VtcHRpb25zIHdlIGhhZCBhYm91dCB0aGUgY29uZm91bmRpbmcgdmFyaWFibGVzIGJlZm9yZS4gU28gZmFyIHRoZSBvY2N1cGF0aW9uIGlzIHRoZSBzdHJvbmdlc3QgcHJlZGljdG9yIG9mIHdhZ2UgbGV2ZWwgYW1vbmcgb3RoZXIgY29uZm91bmRlcnMuIEl0IG1pZ2h0IGFsc28gaW5kaWNhdGUgdGhhdCBlZHVjYXRpb24gaW5kZWVkIHRyYW5zbGF0ZXMgaW50byBoaWdoZXIgd2FnZXMsIGJlY2F1c2UgdGVjaG5pY2FsIGFuZCBtYW5lZ2VyaWFsIHBvc2l0aW9ucyB1c3VhbGx5IHJlcXVpcmUgaGlnaGVyIGxldmVsIG9mIGVkdWNhdGlvbi4gQXQgdGhlIHNhbWUgdGltZSwgdGhlcmUgaXMgc29tZSBhc3NvY2lhdGlvbiB3aXRoIHRoZSBzb2NpYWwgcG9zaXRpb24gb2YgdGhlIGluZGl2aWR1YWw6IGV0aG5pY2l0eSwgbG9jYXRpb24sIGdlbmRlciBhbmQgdW5pb24gbWVtYmVyc2hpcCBhcmUgYWxzbyBhc3NvY2lhdGVkIHdpdGggd2FnZSBsZXZlbC4NCg0KDQojIyBNb2RlbHMgd2l0aCBjb25mb3VuZGVyZCBpbmNsdWRlZA0KDQpMZXQncyBydW4gdGhlIG1vZGVscyB3aXRoIHRoZSBjb25mb3VuZGVycyBpbmNsdWRlZC4gV2UgZG9uJ3QgaW5jbHVkZSBhZ2UgZHVlIHRvIGl0cyBoaWdoIGNvbGxpbmVhcml0eSB3aXRoIGV4cGVyaWVuY2UuDQoNCmBgYHtyfQ0KIyBudW0gKyBnZW5kZXINCm1vZGVsX2Z1bGwxIDwtIGxtKHdhZ2UgfiBlZHVjYXRpb24gKyBleHBlcmllbmNlICsgZ2VuZGVyLCBkYXRhID0gQ1BTKQ0KIyBudW0gKyBldGhuaWNpdHkgDQptb2RlbF9mdWxsMiA8LSBsbSh3YWdlIH4gZWR1Y2F0aW9uICsgZXhwZXJpZW5jZSArIGV0aG5pY2l0eSwgZGF0YSA9IENQUykNCiMgbnVtICsgZ2VuZGVyICsgZXRobmljaXR5IA0KbW9kZWxfZnVsbDMgPC0gbG0od2FnZSB+IGVkdWNhdGlvbiArIGV4cGVyaWVuY2UgKyBnZW5kZXIgKyBldGhuaWNpdHksIGRhdGEgPSBDUFMpDQoNCnN0YXJnYXplcihtb2RlbF9mdWxsMSwgbW9kZWxfZnVsbDIsIG1vZGVsX2Z1bGwzLCB0eXBlID0gInRleHQiLCB0aXRsZSA9ICJXYWdlIExldmVsIHByZWRpY3RvcnMiLCBhbGlnbiA9IFRSVUUpDQpgYGANCkluIHRoZXNlIG1vZGVscyB3ZSBoYXZlIGFkZGVkIHNvY2lhbCBjb25mb3VuZGVycyB0aGF0IGFyZSBoeXBldGhlc2l6ZWQgdG8gYmUgaW1wb3J0YW50Og0KDQotIEVkdWNhdGlvbiBhbmQgZXhwZXJpZW5jZSByZW1haW4gdG8gYmUgc3RhdGlzdGljYWxseSBzaWduaWZpY25hbnQgcHJlZGljdG9ycyBvZiB3YWdlIGxldmVsIGluIGFsbCBtb2RlbHMuDQoNCi0gSW5jbHVkaW5nIGdlbmRlciBhbHNvIHNob3dzIGEgcmF0aGVyIHN0cm9uZyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IHJlbGF0aW9uc2hpcDogYmVpbmcgYSBtYWxlIG1pZ2h0IGJlIGFzc29jaWF0ZWQgd2l0aCBoaWdoZXIgd2FnZXMuDQoNCi0gRXRobmljaXR5IGlzIG5vdCBmb3VuZCB0byBiZSBzaWduaWZpY2FudC4NCg0KTm93LCBsZXQncyBleGFtaW5lIHRoZSBtb2RlbCB3aXRoIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgZm9yIG9jY3VwYXRpb24gaW5jbHVkZWQuIFdlIGV4cGVjdCB0aGF0IHRoaXMgdmFyaWFibGUgd2lsbCBiZSBhIHNpZ25pZmljYW50IHByZWRpY3RvciBvZiB3YWdlIGxldmVsLCBzaW5jZSBpdCBpcyBhIGNvbW1vbiBjYXVzZSBjb25mb3VuZGVyIGZvciB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZWR1Y2F0aW9uIGFuZCB3YWdlcy4NCmBgYHtyfQ0KbW9kZWxfZnVsbDQgPC0gbG0od2FnZSB+IGVkdWNhdGlvbiArIGV4cGVyaWVuY2UgKyBnZW5kZXIgKyBvY2N1cGF0aW9uLCBkYXRhID0gQ1BTKQ0KDQpzdGFyZ2F6ZXIobW9kZWxfZnVsbDEsIG1vZGVsX2Z1bGw0LCB0eXBlID0gInRleHQiLCB0aXRsZSA9ICJXYWdlIExldmVsIHByZWRpY3RvcnMiLCBhbGlnbiA9IFRSVUUpDQpgYGANCkluIHRoZSBsYXN0IG1vZGVsICJzZXJ2aWNlcyIgb2NjdXBhdGlvbiBpcyBkZXNpZ25hdGVkIGFzIGEgcmVmZXJlbmNlIGNhdGVnb3J5LiBUaGUgY29lZmZpY2llbnRzIGZvciB0aGUgb3RoZXIgY2F0ZWdvcmllcyBhcmUgaW50ZXJwcmV0ZWQgYXMgdGhlIGRpZmZlcmVuY2UgaW4gd2FnZSBsZXZlbCBjb21wYXJlZCB0byAic2VydmljZXMiLiBTaW5jZSB0aGUgInNlcnZpY2VzIiBoYWQgdGhlIGxvd2VzdCBtZWRpYW4gYW5kIHRoZSBpbnRlcnF1YXJ0aWxlIHJhbmdlLCBpdCBpcyBhIGdvb2QgZml0IGZvciBhIHJlZmVyZW5jZSBjYXRlZ29yeSBhcyB0aGUgZ3JvdXAgd2l0aCBsb3dlc3QgZWFybmluZ3MuIEFzIGV4cGVjdGVkLCB0ZWNobmljYWwgYW5kIG1hbmFnZXJpYWwgcG9zaXRpb25zIGFyZSBhc3NvY2lhdGVkIHdpdGggaGlnaGVyIHdhZ2VzLCBpbmNyZWFzaW5nIHRoZSB3YWdlIGJ5IH4yLjggYW5kIH4zLjggZG9sbGFycyByZXNwZWN0aXZlbHkgb24gYXZlcmFnZS4gU08gZmFyLCB0aGlzIHJlbGF0aW9uc2hpcCBpcyBoaWdoZXIgdGhhbiBlZHVjYXRpb24sIGV4cGVyaWVuY2UgYW5kIGdlbmRlci4NCg0KVGhlIG1vZGVsIHdpdGggb2NjdXBhdGlvbiBpbmNsdWRlZCBzaG93cyBhIGp1bXAgaW4gUiBzcXVhcmVkIChmcm9tIDAuMjUzIHRvIDAuMzAyKSBhbmQgc2lnbmlmaWNhbnQgY2hhbmdlIGluIHRoZSBjb2VmZmljZW50cyBmb3IgZWR1Y2F0aW9uIGFuZCBnZW5kZXI6IHRoZSBtYWduaXR1ZGUgb2YgYm90aCBjb2VmZmljZW50cyBkZWNyZWFzZWQgYWZ0ZXIgY29udHJvbGxpbmcgZm9yIHRoZSBvY2N1cGF0aW9uLg0KDQpUaGVyZSBpcyBhIGNoYW5jZSBvZiBjb2xsaW5lYXJpdHkgYmV0d2VlbiBvY2N1cGF0aW9uIGFuZCBlZHVjYXRpb24sIHRoYXQgc2hvdWxkIGJlIGNoZWNrZWQ6DQoNCmBgYHtyfQ0KdmlmNCA8LSB2aWYobW9kZWxfZnVsbDQpDQpwcmludCh2aWY0KQ0KYGBgDQpPY2N1cGF0aW9uIHNob3dzIGxvdyBtdWx0aWNvbGxpbmVhcml0eSwgd2hpY2ggaXMgYWNjZXB0YWJsZS4NCg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoImxtdGVzdCIpDQpsaWJyYXJ5KGxtdGVzdCkNCmJwdGVzdChtb2RlbF9mdWxsMSkNCmJwdGVzdChtb2RlbF9mdWxsNCkNCmBgYA0KQnJldXNjaC1QYWdhbiB0ZXN0IGZvciBoZXRlcm9zY2VkYXN0aWNpdHkgc2hvd3MgdGhhdCBib3RoIG1vZGVscyBoYXZlIGhldGVyb3NjZWRhc3RpY2l0eSwgd2hpY2ggbWVhbnMgdGhhdCB0aGUgc3RhbmRhcmQgZXJyb3JzIGFyZSBub3QgY29uc3RhbnQuIEl0IG1pZ2h0IGJlIGEgc2lnbiBvZiBvbWl0dGVkIHZhcmlhYmxlcy4NCg0KYGBge3J9DQpwbG90KG1vZGVsX2Z1bGw0JGZpdHRlZC52YWx1ZXMsIG1vZGVsX2Z1bGw0JHJlc2lkdWFscywNCiAgICAgbWFpbiA9ICJSZXNpZHVhbHMgdnMgRml0dGVkOiBNb2RlbCB3aXRoIEdlbmRlciAmIE9jY3VwYXRpb24iLA0KICAgICB4bGFiID0gIkZpdHRlZCBWYWx1ZXMiLA0KICAgICB5bGFiID0gIlJlc2lkdWFscyIpDQphYmxpbmUoaCA9IDAsIGNvbCA9ICJyZWQiKQ0KYGBgDQpMZXQncyBub3cgYWRkIG5ldyBjb25mb3VuZGVycyB0byB0aGUgbW9kZWwgNC4NCg0KYGBge3J9DQptb2RlbF9mdWxsNSA8LSBsbSh3YWdlIH4gZWR1Y2F0aW9uICsgZXhwZXJpZW5jZSArIGdlbmRlciArIG9jY3VwYXRpb24gKyBzZWN0b3IsIGRhdGEgPSBDUFMpDQpzdGFyZ2F6ZXIobW9kZWxfZnVsbDQgLG1vZGVsX2Z1bGw1LCB0eXBlID0gInRleHQiLCB0aXRsZSA9ICJXYWdlIExldmVsIHByZWRpY3RvcnMiLCBhbGlnbiA9IFRSVUUpDQpgYGANCkFkZGluZyBzZWN0b3IgYXMgYSBwcmVkaWN0b3IgZG9lcyBub3Qgc2VlbSB0byBwcm9kdWNlIGFueSBzaWduaWZpY2FudCBjaGFuZ2VzIGluIHRoZSBtb2RlbC4gVGhlIGNvZWZmaWNpZW50cyBmb3Igb3RoZXIgcHJlZGljdG9ycyByZW1haW4gcXVpdGUgc2ltaWxhciwgd2hpbGUgc2VjdG9yIGR1bW15IHZhcmlhYmxlcyBhcmUgbm90IHNpZ25pZmljYW50Lg0KDQpgYGB7cn0NCm1vZGVsX2Z1bGw2IDwtIGxtKHdhZ2UgfiBlZHVjYXRpb24gKyBleHBlcmllbmNlICsgZ2VuZGVyICsgb2NjdXBhdGlvbiArIHJlZ2lvbiwgZGF0YSA9IENQUykNCnN0YXJnYXplcihtb2RlbF9mdWxsNCwgbW9kZWxfZnVsbDYsIHR5cGUgPSAidGV4dCIsIHRpdGxlID0gIldhZ2UgTGV2ZWwgcHJlZGljdG9ycyIsIGFsaWduID0gVFJVRSkNCmBgYA0KQWRkaW5nIHJlZ2lvbiAod2hpY2ggc2hvd3MgZWl0aGVyIGxpdmluZyBpbiB0aGUgTm9ydGggb3IgaW4gdGhlIFNvdXRoIG9mIHRoZSBVUykgYXMgYSBwcmVkaWN0b3Igc2hvd3MgdGhhdCBsaXZpbmcgaW4gdGhlIFNvdXRoIGlzIGFzc29jaWF0ZWQgd2l0aCBsb3dlciB3YWdlcy4gSXQgaGFzIGFsc28gc2xpZ2hseSBhbHRlcmVkIHRoZSBtYWduaXR1ZGUgb2Ygb3RoZXIgcHJlZGljdG9ycywgYnV0IHRoZWlyIGNvZWZmaWNpZW50cyByZW1haW4gc2lnbmlmaWNhbnQuDQoNCg0KYGBge3J9DQptb2RlbF9mdWxsNyA8LSBsbSh3YWdlIH4gZWR1Y2F0aW9uICsgZXhwZXJpZW5jZSArIGdlbmRlciArIG9jY3VwYXRpb24gKyByZWdpb24gKyB1bmlvbiwgZGF0YSA9IENQUykNCnN0YXJnYXplcihtb2RlbF9mdWxsNiwgbW9kZWxfZnVsbDcsIHR5cGUgPSAidGV4dCIsIHRpdGxlID0gIldhZ2UgTGV2ZWwgcHJlZGljdG9ycyIsIGFsaWduID0gVFJVRSkNCmBgYA0KDQpCZWluZyBhIHVuaW9uIG1lbWJlciBhcHBlYXJzIHRvIGhhdmUgYSBzaWduaWZpY2FudCBwb3NhaXRpdmUgcmVsYXRpb25zaGlwIHdpdGggdGhlIHdhZ2UgbGV2ZWwgKGFzIHdhcyB0aGVvcml6ZWQgYmVmb3JlIGNvbnN0cnVjdGluZyB0aGUgbW9kZWwpLiBBZGRpbmcgcHJlZGljdG9yIGZvciB1bmlvbiBtZW1iZXJzaGlwIGhhcyBhbHNvIHNsaWdodGx5IGFsdGVyZWQgdGhlIG1hZ25pdHVkZSBvZiBvdGhlciBwcmVkaWN0b3JzLCBidXQgdGhlaXIgY29lZmZpY2llbnRzIHN0aWxsIHJlbWFpbiBzaWduaWZpY2FudC4NCg0KSW50ZXJlc3RpbmdseSwgdGhlIG1hZ25pdHVkZSBhZmZlY3RlZCB0aGUgbW9zdCBpcyB0aGUgb2NjdXBhdGlvbjogdGhlIGNvZWZmaWNpZW50cyBmb3IgbWFuYWdlcmlhbCBwb3NpdGlvbnMgaGF2ZSBpbmNyZWFzZWQuIA0KDQpgYGB7cn0NCm1vZGVsX2Z1bGw4IDwtIGxtKHdhZ2UgfiBlZHVjYXRpb24gKyBleHBlcmllbmNlICsgZ2VuZGVyICsgb2NjdXBhdGlvbiArIHJlZ2lvbiArIHVuaW9uICsgbWFycmllZCwgZGF0YSA9IENQUykNCnN0YXJnYXplcihtb2RlbF9mdWxsNywgbW9kZWxfZnVsbDgsIHR5cGUgPSAidGV4dCIsIHRpdGxlID0gIldhZ2UgTGV2ZWwgcHJlZGljdG9ycyIsIGFsaWduID0gVFJVRSkNCmBgYA0KQmVpbmcgbWFycmllZCBkb2VzIG5vdCBhcHBlYXIgdG8gYmUgYSBzaWduaWZpY2FudCBwcmVkaWN0b3Igb2Ygd2FnZSBsZXZlbCBpbiB0aGlzIG1vZGVsLCB3aGljaCBpcyBjb25zaXN0ZW50IHdpdGggb3VyIHByZWxpbWluYXJ5IGFzc3VtcHRpb25zLg0KDQotIEhldGVyc2NlZGFzdGljaXR5Pw0KDQoNCiMjDQoNCkVkdWNhdGlvbiwgZXhwZXJpZW5jZSwgZ2VuZGVyLCBvY2N1cGF0aW9uIChpbiB0ZWNobmljYWwsIG1hbmFnZW1lbnQgcm9sZXMpLCBhbmQgdW5pb24gbWVtYmVyc2hpcCBhcmUgc2lnbmlmaWNhbnQgcHJlZGljdG9ycyBvZiB3YWdlcy4gDQoNCmBgYHtyfQ0KbGlicmFyeShjYXIpDQpwcmludCh2aWYobW9kZWxfZnVsbCkpDQpgYGANClZJRiB2YWx1ZXMgYXJlIHF1aXRlIGxvdywgd2hpY2ggaW5kaWNhdGVzIHRoYXQgbXVsdGljb2xsaW5lYXJpdHkgaXMgbm90IGFuIGlzc3VlIGluIHRoaXMgbW9kZWwsIGFmdGVyIGV4Y2x1ZGluZyBhZ2UuDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQptb2RlbF9mdWxsX3Jlc2lkdWFscyA8LSBnZ3Bsb3QoQ1BTLCBhZXMoeCA9IGZpdHRlZChtb2RlbF9mdWxsKSwgeSA9IHJlc2lkKG1vZGVsX2Z1bGwpKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArDQogIGxhYnMoeCA9ICJGaXR0ZWQgVmFsdWVzIiwgeSA9ICJSZXNpZHVhbHMiLCB0aXRsZSA9ICJSZXNpZHVhbHMgdnMuIEZpdHRlZCBWYWx1ZXMgUGxvdCIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdncGxvdGx5KG1vZGVsX2Z1bGxfcmVzaWR1YWxzLCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQ0KYGBgDQpUaGVyZSBpcyB3aWRlbmluZz8NCg0KDQoNCg0KDQpUaGVyZSdzIGEgbnVtYmVyIG9mIHZhcmlhYmxlcyB0aGF0IGFwcGVhcmVkIHRvIGJlIG5vbi1zaWduaWZpY2FudDogc2VjdG9yICYgbWFycmlhZ2UuIExldCdzIGNvbnN0cmN1dCBhIG1vZGVsIHdpdGhvdXQgdGhlbSBhbmQgY29tcGFyZSB0aGUgcmVzdWx0cy4NCg0KYGBge3J9DQptb2RlbF93b19tYXJyaWFnZV9zZWN0b3JfcmVnaW9uIDwtIGxtKHdhZ2UgfiBlZHVjYXRpb24gKyBleHBlcmllbmNlICsgZXRobmljaXR5ICsgIGdlbmRlciArIG9jY3VwYXRpb24gKyB1bmlvbiwgZGF0YSA9IENQUykNCg0Kc3RhcmdhemVyKG1vZGVsX2Z1bGwsIG1vZGVsX3dvX21hcnJpYWdlX3NlY3Rvcl9yZWdpb24sIHR5cGUgPSAidGV4dCIsIHRpdGxlID0gIldhZ2UgTGV2ZWwgcHJlZGljdG9ycyIsIGFsaWduID0gVFJVRSkNCmBgYA0KDQoNCg0KIyMgQ2hlY2sgTW9kZWwgRml0IGFuZCBEaWFnbm9zdGljcw0KMS4gb3ZlcmZpdHRpbmc/DQoyLiBDaGVjayByZXNpZHVhbHMgdG8gZW5zdXJlIHRoZSBhc3N1bXB0aW9ucyBvZiBsaW5lYXJpdHksIGhvbW9zY2VkYXN0aWNpdHksIGFuZCBub3JtYWxpdHkgaG9sZC4NCjMuICB5IC0geSBwbG90DQo0LiBDb25zaWRlciBJbnRlcmFjdGlvbiBFZmZlY3RzDQoNCg0KRG8gb3V0bGllcnMgYWZmZWN0IHRoZSByZXN1bHRzPw0KLSBDb29rJ3MgRGlzdGFuY2UgDQotIFJvYnVzdG5lc3MgY2hlY2tzDQoNCiMjIExpbWl0YXRpb25zDQoNCi0geSBpcyBub3QgdHJ1bHkgcmFuZG9tDQoNCg==